am c0c7cebe: am b566c466: Merge "Donot bind to GATT service when BLE is not supported" into jb-mr2-dev
* commit 'c0c7cebe3f5da99aeb48eb62bffbaf3183814194':
Donot bind to GATT service when BLE is not supported
diff --git a/Android.mk b/Android.mk
index 3b7d834..6ceb785 100644
--- a/Android.mk
+++ b/Android.mk
@@ -161,6 +161,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 +187,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/api/current.txt b/api/current.txt
index c1caaf2..7200dad 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -70,6 +70,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";
@@ -513,6 +514,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 = 16843740; // 0x10103dc
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 +629,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 = 16843738; // 0x10103da
field public static final int killAfterRestore = 16843420; // 0x101029c
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
@@ -1012,6 +1015,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 = 16843739; // 0x10103db
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
field public static final int taskAffinity = 16842770; // 0x1010012
@@ -1100,6 +1104,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 = 16843741; // 0x10103dd
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
field public static final int toYDelta = 16843209; // 0x10101c9
@@ -1114,6 +1119,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 = 16843742; // 0x10103de
field public static final int translationX = 16843554; // 0x1010322
field public static final int translationY = 16843555; // 0x1010323
field public static final int type = 16843169; // 0x10101a1
@@ -6042,6 +6048,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";
@@ -6084,6 +6091,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";
@@ -6128,6 +6136,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";
@@ -6191,6 +6200,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";
@@ -6242,6 +6252,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
@@ -6599,6 +6610,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);
@@ -7158,7 +7222,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;
@@ -9697,7 +9761,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);
@@ -9717,6 +9780,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();
@@ -11405,7 +11469,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();
@@ -11427,7 +11491,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);
@@ -12407,7 +12471,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);
@@ -12417,14 +12481,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";
@@ -16779,6 +16843,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 {
@@ -19504,6 +19569,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";
@@ -23530,7 +23617,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);
@@ -25928,6 +26017,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();
@@ -26357,6 +26447,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
@@ -28075,6 +28166,146 @@
}
+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 protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ 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 protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ 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);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public class Rotate extends android.view.transition.Transition {
+ ctor public Rotate();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ 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);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public abstract class Transition {
+ 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 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 void onTransitionCancel();
+ method protected void onTransitionEnd();
+ method protected void onTransitionStart();
+ method protected abstract android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean prePlay(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 protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ 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 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.HashMap 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.View, int, android.view.View, int);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean preAppear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected boolean preDisappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ }
+
+}
+
package android.webkit {
public class ConsoleMessage {
@@ -29414,6 +29645,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();
@@ -29423,6 +29655,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);
@@ -30694,6 +30927,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();
@@ -30792,6 +31026,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..ce71e4a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -75,6 +75,7 @@
(new Am()).run(args);
}
+ @Override
public void onShowUsage(PrintStream out) {
out.println(
"usage: am [subcommand] [options]\n" +
@@ -101,6 +102,9 @@
" 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_ID> <POSITION> <WEIGHT>\n" +
+ " am stack movetask <STACK_ID> <TASK_ID> [true|false]\n" +
+ " am stack dump\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -186,6 +190,17 @@
"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_ID>: existing stack's id.\n" +
+ " <POSITION>: 0: to left of, 1: to right of, 2: above, 3: below\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 dump: list the hierarchy of stacks.\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 +233,7 @@
);
}
+ @Override
public void onRun() throws Exception {
mAm = ActivityManagerNative.getDefault();
@@ -266,6 +282,8 @@
runSwitchUser();
} else if (op.equals("stop-user")) {
runStopUser();
+ } else if (op.equals("stack")) {
+ runStack();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -1036,7 +1054,7 @@
}
@Override
- public boolean activityResuming(String pkg) throws RemoteException {
+ public boolean activityResuming(String pkg) {
synchronized (this) {
System.out.println("** Activity resuming: " + pkg);
}
@@ -1044,7 +1062,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 +1071,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 +1088,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 +1101,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);
@@ -1361,7 +1377,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 +1410,7 @@
mRawMode = rawMode;
}
+ @Override
public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
synchronized (this) {
// pretty printer mode?
@@ -1416,6 +1433,7 @@
}
}
+ @Override
public void instrumentationFinished(ComponentName name, int resultCode,
Bundle results) {
synchronized (this) {
@@ -1456,4 +1474,67 @@
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("dump")) {
+ runStackDump();
+ } 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 runStackDump() throws Exception {
+ try {
+ List<ActivityManager.StackInfo> stacks = mAm.getStacks();
+ for (ActivityManager.StackInfo stack : stacks) {
+ System.out.println(stack);
+ }
+ } 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/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 745c34a..9df2ea2 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>
@@ -94,12 +94,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/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..f8ae616 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,7 +1024,7 @@
mStarted = false;
mStartListenersCalled = false;
mPlayingBackwards = false;
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "animator",
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
@@ -1033,7 +1033,7 @@
* called on the UI thread.
*/
private void startAnimation(AnimationHandler handler) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "animator",
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
initAnimation();
handler.mAnimations.add(this);
@@ -1045,6 +1045,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/ActivityManager.java b/core/java/android/app/ActivityManager.java
index bb9e19f..729ebd7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -31,9 +31,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;
@@ -1229,7 +1228,74 @@
} catch (RemoteException e) {
}
}
-
+
+
+ /**
+ * 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;
+
+ public StackInfo() {
+ }
+
+ @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];
+ }
+ };
+
+ private StackInfo(Parcel source) {
+ readFromParcel(source);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append("Stack id="); sb.append(stackId);
+ sb.append(" bounds="); sb.append(bounds.toShortString()); sb.append("\n");
+ final String 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();
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d4478bf..b197f90 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -107,7 +107,8 @@
public ActivityManagerNative() {
attachInterface(this, descriptor);
}
-
+
+ @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
@@ -197,7 +198,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 +224,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 +237,7 @@
reply.writeInt(result);
return true;
}
-
+
case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -267,7 +268,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 +479,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 +535,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 +554,7 @@
reply.writeTypedList(list);
return true;
}
-
+
case GET_RUNNING_APP_PROCESSES_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
List<ActivityManager.RunningAppProcessInfo> list = getRunningAppProcesses();
@@ -609,6 +608,53 @@
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 stackId = data.readInt();
+ float weight = data.readFloat();
+ resizeStack(stackId, weight);
+ reply.writeNoException();
+ return true;
+ }
+
+ case GET_STACKS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ List<ActivityManager.StackInfo> list = getStacks();
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ 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 +1079,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 +1091,7 @@
reply.writeInt(res ? 1 : 0);
return true;
}
-
+
case GRANT_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1057,7 +1103,7 @@
reply.writeNoException();
return true;
}
-
+
case REVOKE_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1068,7 +1114,7 @@
reply.writeNoException();
return true;
}
-
+
case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -1257,7 +1303,7 @@
reply.writeNoException();
return true;
}
-
+
case FORCE_STOP_PACKAGE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String packageName = data.readString();
@@ -2565,6 +2611,77 @@
data.recycle();
reply.recycle();
}
+ @Override
+ public int createStack(int taskId, int relativeStackId, int position, float weight)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(relativeStackId);
+ 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 resizeStack(int stackId, float weight) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ data.writeFloat(weight);
+ mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
+ public List<ActivityManager.StackInfo> getStacks() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_STACKS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<ActivityManager.StackInfo> list
+ = reply.createTypedArrayList(ActivityManager.StackInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
+ @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();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c9776f1..948210c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -95,8 +95,17 @@
public static final int OP_PLAY_AUDIO = 28;
public static final int OP_READ_CLIPBOARD = 29;
public static final int OP_WRITE_CLIPBOARD = 30;
+ public static final int OP_TAKE_MEDIA_BUTTONS = 31;
+ public static final int OP_TAKE_AUDIO_FOCUS = 32;
+ public static final int OP_AUDIO_MASTER_VOLUME = 33;
+ public static final int OP_AUDIO_VOICE_VOLUME = 34;
+ public static final int OP_AUDIO_RING_VOLUME = 35;
+ public static final int OP_AUDIO_MEDIA_VOLUME = 36;
+ public static final int OP_AUDIO_ALARM_VOLUME = 37;
+ public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
+ public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
/** @hide */
- public static final int _NUM_OP = 31;
+ public static final int _NUM_OP = 40;
/**
* This maps each operation to the operation that serves as the
@@ -138,6 +147,15 @@
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,
};
/**
@@ -176,6 +194,15 @@
"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",
};
/**
@@ -214,6 +241,15 @@
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
};
/**
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a21caee..334a304 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.StackInfo;
import android.content.ComponentName;
import android.content.ContentProviderNative;
import android.content.IContentProvider;
@@ -99,19 +102,25 @@
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 relativeStackId, int position, float weight)
+ throws RemoteException;
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
+ public void resizeStack(int stackId, float weight) throws RemoteException;
+ public List<StackInfo> getStacks() 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 +158,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 +177,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 +190,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;
@@ -395,10 +404,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 +423,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 +454,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 +470,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];
}
@@ -641,4 +658,9 @@
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_STACKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
+ int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171;
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index c62bf32..3a355f9 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/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index fefd343..b3f0d96 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -220,6 +220,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 +1833,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 +1873,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..81d6f0b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2434,7 +2434,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".
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bfc7bf5..793736d 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.
@@ -2596,6 +2595,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()).
@@ -3199,6 +3238,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).
@@ -3248,6 +3294,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
@@ -7036,7 +7091,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/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/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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 34e0c12..1b997f0 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;
@@ -714,6 +720,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 +1040,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 +1536,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 +3164,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 +3196,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;
@@ -3360,6 +3448,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;
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/Resources.java b/core/java/android/content/res/Resources.java
index 42f4faf..c7976c3 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,6 +28,7 @@
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -1985,6 +1984,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/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4e51080..ac42b76 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());
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 499ec77..d0f7511 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -98,6 +98,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 +130,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 = 6;
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -137,6 +142,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";
@@ -276,6 +282,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 +291,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();
/**
@@ -1229,7 +1238,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",
@@ -1417,22 +1426,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);
}
}
}
@@ -1961,6 +1979,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 +2004,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 +2081,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);
@@ -2215,7 +2254,7 @@
* @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dumpLocked(PrintWriter pw) {
+ public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly) {
prepareForDumpLocked();
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
@@ -2267,28 +2306,22 @@
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, -1);
+ pw.println("");
+ }
pw.println("Statistics since last unplugged:");
dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, -1);
}
@SuppressWarnings("unused")
- public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
+ public void dumpCheckinLocked(
+ PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly) {
prepareForDumpLocked();
-
- boolean isUnpluggedOnly = false;
-
- for (String arg : args) {
- if ("-u".equals(arg)) {
- if (LOCAL_LOGV) Log.v("BatteryStats", "Dumping unplugged data");
- isUnpluggedOnly = true;
- }
- }
if (apps != null) {
SparseArray<ArrayList<String>> uids = new SparseArray<ArrayList<String>>();
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/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/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/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/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 38fb51b..163709a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1011,6 +1011,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 */
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/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 2ec9a7d..cc7d948 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;
@@ -273,6 +274,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 +327,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 +351,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 +364,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
///////////////////////////////////////////////////////////////////////////
@@ -718,20 +736,21 @@
}
@Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+ 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, chunks,
+ nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk,
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,
+ private static native void nDrawPatch(int renderer, int bitmap, byte[] chunks,
float left, float top, float right, float bottom, int paint);
@Override
@@ -741,14 +760,14 @@
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- 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,
+ float left, float top, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
@@ -757,15 +776,13 @@
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
- matrix.native_instance, nativePaint);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
- int matrix, int paint);
+ private static native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
@@ -787,7 +804,7 @@
bottom = src.bottom;
}
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
@@ -814,14 +831,14 @@
bottom = src.bottom;
}
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
+ private static native void nDrawBitmap(int renderer, int bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float left, float top, float right, float bottom, int paint);
@@ -891,14 +908,14 @@
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
try {
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+ nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
verts, vertOffset, colors, colorOffset, nativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
- private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
+ private static native void nDrawBitmapMesh(int renderer, int bitmap,
int meshWidth, int meshHeight, float[] verts, int vertOffset,
int[] colors, int colorOffset, int paint);
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 3272504..d367267 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import java.util.ArrayList;
@@ -29,7 +30,8 @@
// 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<Bitmap> mBitmaps = new ArrayList<Bitmap>(10);
+ final ArrayList<NinePatch> mNinePatches = new ArrayList<NinePatch>(10);
final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
private GLES20RecordingCanvas mCanvas;
@@ -83,7 +85,12 @@
}
mValid = false;
+ clearReferences();
+ }
+
+ void clearReferences() {
mBitmaps.clear();
+ mNinePatches.clear();
mChildDisplayLists.clear();
}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7da2451..ec059d5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -19,6 +19,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Matrix;
+import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -62,8 +63,7 @@
}
void start() {
- mDisplayList.mBitmaps.clear();
- mDisplayList.mChildDisplayLists.clear();
+ mDisplayList.clearReferences();
}
int end(int nativeDisplayList) {
@@ -80,9 +80,10 @@
}
@Override
- public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
- super.drawPatch(bitmap, chunks, dst, paint);
- mDisplayList.mBitmaps.add(bitmap);
+ public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ super.drawPatch(patch, dst, paint);
+ mDisplayList.mBitmaps.add(patch.getBitmap());
+ mDisplayList.mNinePatches.add(patch);
// Shaders in the Paint are ignored when drawing a Bitmap
}
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..b4576f3
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+ /**
+ * 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>
+ *
+ * @return A Canvas used to draw into the buffer, or null.
+ *
+ * @see #lockCanvas(android.graphics.Rect)
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ */
+ public Canvas lockCanvas() {
+ return lockCanvas(null);
+ }
+
+ /**
+ * Just like {@link #lockCanvas()} but allows specification of a dirty
+ * rectangle.
+ *
+ * @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)
+ */
+ public Canvas lockCanvas(Rect dirty) {
+ if (mCanvas == null) {
+ mCanvas = new Canvas();
+ }
+
+ if (nLockCanvas(mNativeObject, mCanvas, dirty)) {
+ mSaveCount = mCanvas.save();
+ return mCanvas;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finish editing pixels in the buffer.
+ *
+ * @param canvas The Canvas previously returned by lockCanvas()
+ *
+ * @see #lockCanvas()
+ * @see #lockCanvas(android.graphics.Rect)
+ */
+ public void unlockCanvasAndPost(Canvas canvas) {
+ if (mCanvas != null && canvas == mCanvas) {
+ canvas.restoreToCount(mSaveCount);
+ mSaveCount = 0;
+
+ nUnlockCanvasAndPost(mNativeObject, mCanvas);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nDestroyGraphicBuffer(mNativeObject);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ 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/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..e8c1653 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.
@@ -762,6 +782,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 +838,9 @@
Paint mProfilePaint;
boolean mDebugDirtyRegions;
- boolean mShowOverdraw;
+ int mDebugOverdraw = -1;
+ HardwareLayer mDebugOverdrawLayer;
+ Paint mDebugOverdrawPaint;
final int mGlVersion;
final boolean mTranslucent;
@@ -826,18 +859,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 +922,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 +944,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 +1011,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 +1019,7 @@
@Override
boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
if (isRequested() && !isEnabled()) {
- initializeEgl();
+ boolean contextCreated = initializeEgl();
mGl = createEglSurface(surface);
mDestroyed = false;
@@ -991,6 +1034,10 @@
mCanvas.setName(mName);
}
setEnabled(true);
+
+ if (contextCreated) {
+ initAtlas();
+ }
}
return mCanvas != null;
@@ -1010,7 +1057,7 @@
abstract int[] getConfig(boolean dirtyRegions);
- void initializeEgl() {
+ boolean initializeEgl() {
synchronized (sEglLock) {
if (sEgl == null && sEglConfig == null) {
sEgl = (EGL10) EGLContext.getEGL();
@@ -1043,7 +1090,10 @@
if (mEglContext == null) {
mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
sEglContextStorage.set(createManagedContext(mEglContext));
+ return true;
}
+
+ return false;
}
private EGLConfig loadEglConfig() {
@@ -1181,6 +1231,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 +1244,7 @@
"Could not create an EGL context. eglCreateContext failed with error: " +
GLUtils.getEGLErrorString(sEgl.eglGetError()));
}
+
return context;
}
@@ -1380,6 +1432,8 @@
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false;
+ debugOverdraw(attachInfo, dirty, canvas, displayList);
+
mFrameCount++;
debugDirtyRegions(dirty, canvas);
@@ -1399,6 +1453,61 @@
}
}
+ 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) {
+ // TODO: Use an alpha layer allocated from a GraphicBuffer
+ // The alpha format will help with rendering performance and
+ // the GraphicBuffer will let us skip the read pixels step
+ 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) {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
@@ -1788,7 +1897,29 @@
@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 {
+ GraphicBuffer buffer = atlas.getBuffer();
+ if (buffer != null) {
+ int[] map = atlas.getMap();
+ if (map != null) {
+ GLES20Canvas.initAtlas(buffer, map);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Could not acquire atlas", e);
+ }
}
@Override
@@ -1985,6 +2116,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();
}
diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl
new file mode 100644
index 0000000..2595179
--- /dev/null
+++ b/core/java/android/view/IAssetAtlas.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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 {
+ /**
+ * 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..8007d9a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -80,8 +80,8 @@
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);
@@ -103,9 +103,6 @@
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/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/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 5c3934d..f0acba1 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -648,13 +648,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;
@@ -664,7 +670,9 @@
}
synchronized (mNativeWindowLock) {
- nLockCanvas(mNativeWindow, mCanvas, dirty);
+ if (!nLockCanvas(mNativeWindow, mCanvas, dirty)) {
+ return null;
+ }
}
mSaveCount = mCanvas.save();
@@ -803,6 +811,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 c47e111..782f817 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -73,6 +73,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.*;
@@ -1572,6 +1573,8 @@
*/
protected Object mTag;
+ private Scene mCurrentScene = null;
+
// for mPrivateFlags:
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
@@ -2341,7 +2344,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 +2362,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 +2491,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;
@@ -12040,9 +12076,14 @@
mCurrentAnimation = null;
+ mCurrentScene = null;
+
resetAccessibilityStateChanged();
}
+ void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
+ }
+
/**
* @return The number of times this view has been attached to a window
*/
@@ -17758,6 +17799,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
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1fef0a2..f21c63e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -204,7 +204,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
@@ -345,6 +345,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}
@@ -373,6 +381,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},
@@ -389,7 +399,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
@@ -531,7 +541,7 @@
}
break;
case R.styleable.ViewGroup_layoutMode:
- setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE));
+ setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
break;
}
}
@@ -3447,6 +3457,24 @@
}
}
+ private void clearCachedLayoutMode() {
+ if (!getBooleanFlag(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).
@@ -4750,6 +4778,10 @@
setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
}
+ private boolean getBooleanFlag(int flag) {
+ return (mGroupFlags & flag) == flag;
+ }
+
private void setBooleanFlag(int flag, boolean value) {
if (value) {
mGroupFlags |= flag;
@@ -4793,24 +4825,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 ||
+ getBooleanFlag(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
*
@@ -4818,7 +4889,8 @@
*/
public void setLayoutMode(int layoutMode) {
if (mLayoutMode != layoutMode) {
- mLayoutMode = layoutMode;
+ invalidateInheritedLayoutMode(layoutMode);
+ setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
requestLayout();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b63ccab..fc005d1 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_MEDIA_DISABLED = "config.disable_media";
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -130,10 +128,6 @@
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;
@@ -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;
@@ -373,35 +369,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) {
@@ -479,7 +446,7 @@
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
- enableHardwareAcceleration(mView.getContext(), attrs);
+ enableHardwareAcceleration(attrs);
}
boolean restore = false;
@@ -687,7 +654,7 @@
}
}
- private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
+ private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -727,11 +694,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);
}
@@ -5057,6 +5019,10 @@
public void playSoundEffect(int effectId) {
checkThread();
+ if (mMediaDisabled) {
+ return;
+ }
+
try {
final AudioManager audioManager = getAudioManager();
@@ -5202,6 +5168,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())) {
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/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c0044b6..b9f9e80 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -816,6 +816,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/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/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 7ec5398..b3ff54d 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -268,8 +268,9 @@
if (content != null) {
beginBatchEdit();
removeComposingSpans(content);
- endBatchEdit();
+ // Note: sendCurrentText does nothing unless mDummyMode is set
sendCurrentText();
+ endBatchEdit();
}
return true;
}
@@ -466,8 +467,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..d94cf2c
--- /dev/null
+++ b/core/java/android/view/transition/AutoTransition.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.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..babf58f
--- /dev/null
+++ b/core/java/android/view/transition/Crossfade.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 java.util.HashMap;
+
+/**
+ * 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();
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = startValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
+ Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
+ Drawable startDrawable = (Drawable) startVals.get(PROPNAME_DRAWABLE);
+ Drawable endDrawable = (Drawable) 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)) {
+ view.getOverlay().add(endDrawable);
+ view.getOverlay().add(startDrawable);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+
+ final View view = endValues.view;
+ Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
+ final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
+ final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
+
+ // 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());
+ }
+ });
+ 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) {
+ view.getOverlay().remove(startDrawable);
+ view.getOverlay().remove(endDrawable);
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(anim);
+ if (!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);
+ Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim2);
+ set.playTogether(anim3);
+ }
+ return set;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ values.values.put(PROPNAME_BOUNDS, new Rect(0, 0,
+ view.getWidth(), view.getHeight()));
+
+ 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(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ 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..8e4909d
--- /dev/null
+++ b/core/java/android/view/transition/Fade.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.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";
+
+ /**
+ * 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 boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return false;
+ }
+ endView.setAlpha(0);
+ return true;
+ }
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return null;
+ }
+ // TODO: hack - retain original value from before preAppear
+ return runAnimation(endView, 0, 1, null);
+ // TODO: end listener to make sure we end at 1 no matter what
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return false;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ 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;
+ }
+ }
+ }
+ // 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
+ sceneRoot.getOverlay().add(overlayView);
+ return true;
+ }
+ 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));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return null;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.disappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ View overlayView = null;
+ View viewToKeep = null;
+ final int finalVisibility = endVisibility;
+ 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;
+ }
+ }
+ }
+ // 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);
+ }
+
+}
\ 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..3bd57bd
--- /dev/null
+++ b/core/java/android/view/transition/Move.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * 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 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;
+ }
+ final View view = endValues.view;
+ if (view.getParent() == null) {
+ // TODO: Might want to make it possible to Move an disappearing view.
+ // This workaround is here because if a parallel Fade is not running on the view
+ // Then it won't get added to the hierarchy and the animator below will not fire,
+ // causing the transition to not end
+ return null;
+ }
+ // TODO: need to handle non-VG case?
+ ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return null;
+ }
+ boolean parentsEqual = (startParent == endParent) ||
+ (startParent.getId() == endParent.getId());
+ if (!mReparent || parentsEqual) {
+ // Common case - view belongs to the same layout before/after. Just animate its bounds
+ 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) {
+ 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 {
+ // 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 {
+ return (ObjectAnimator) endValues.values.get("drawableAnim");
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean prePlay(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ HashMap<String, Object> startParentVals = startValues.values;
+ HashMap<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 false;
+ }
+ 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) {
+ if (startLeft != endLeft) view.setLeft(startLeft);
+ if (startTop != endTop) view.setTop(startTop);
+ if (startRight != endRight) view.setRight(startRight);
+ if (startBottom != endBottom) view.setBottom(startBottom);
+ } 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);
+ }
+ return true;
+ }
+ } 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 startBounds = new Rect(startX - tempLocation[0], startY - tempLocation[1],
+ startX - tempLocation[0] + view.getWidth(),
+ startY - tempLocation[1] + view.getHeight());
+ Rect endBounds = 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, startBounds, endBounds);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sceneRoot.getOverlay().remove(drawable);
+ view.setVisibility(View.VISIBLE);
+ }
+ });
+ endParentVals.put("drawableAnim", anim);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/view/transition/Recolor.java b/core/java/android/view/transition/Recolor.java
new file mode 100644
index 0000000..7048be9
--- /dev/null
+++ b/core/java/android/view/transition/Recolor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * 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 boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ 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;
+ }
+ }
+ 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 changed;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ ObjectAnimator anim = null;
+ final View view = endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Drawable startBackground = (Drawable) startVals.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endVals.get(PROPNAME_BACKGROUND);
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ anim = ObjectAnimator.ofObject(endBackground, "color",
+ new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
+ if (getStartDelay() > 0) {
+ endColor.setColor(startColor.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) {
+ anim = ObjectAnimator.ofObject(textView, "textColor",
+ new ArgbEvaluator(), start, end);
+ if (getStartDelay() > 0) {
+ textView.setTextColor(end);
+ }
+ }
+ }
+ return anim;
+ }
+}
diff --git a/core/java/android/view/transition/Rotate.java b/core/java/android/view/transition/Rotate.java
new file mode 100644
index 0000000..b42fbe5
--- /dev/null
+++ b/core/java/android/view/transition/Rotate.java
@@ -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.
+ */
+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 boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ 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 true;
+ }
+ return false;
+ }
+
+ @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) {
+ 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..62cb9d3
--- /dev/null
+++ b/core/java/android/view/transition/Scene.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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..8630ee2
--- /dev/null
+++ b/core/java/android/view/transition/Slide.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 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, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(endView, View.TRANSLATION_Y,
+ -2 * endView.getHeight(), 0);
+ anim.setInterpolator(sDecelerator);
+ return anim;
+ }
+
+ @Override
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ endView.setTranslationY(-2 * endView.getHeight());
+ return true;
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ startView.setTranslationY(0);
+ return true;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ 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..0ba2412
--- /dev/null
+++ b/core/java/android/view/transition/TextChange.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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * 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 boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return false;
+ }
+ final TextView view = (TextView) endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ String startText = (String) startVals.get(PROPNAME_TEXT);
+ String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ view.setText(startText);
+ return true;
+ }
+ return false;
+ }
+
+ @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;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ final String startText = (String) startVals.get(PROPNAME_TEXT);
+ final String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ // This noop animation is just used to keep the text in its start state
+ // until the transition ends
+ 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..150c218
--- /dev/null
+++ b/core/java/android/view/transition/Transition.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.LongSparseArray;
+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;
+import java.util.HashMap;
+
+/**
+ * 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 {
+
+ 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;
+ // TODO: sparse arrays instead of hashmaps?
+ private HashMap<View, TransitionValues> mStartValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mStartIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mStartItemIdValues =
+ new LongSparseArray<TransitionValues>();
+ private HashMap<View, TransitionValues> mEndValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mEndIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mEndItemIdValues =
+ new LongSparseArray<TransitionValues>();
+
+ // Used to carry data between preplay() and play(), cleared before every scene transition
+ private ArrayList<TransitionValues> mPlayStartValuesList = new ArrayList<TransitionValues>();
+ private ArrayList<TransitionValues> mPlayEndValuesList = new ArrayList<TransitionValues>();
+
+ // 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 run animations on its target
+ * views. The method is called for every applicable target object, which
+ * is stored in the {@link TransitionValues#view} field. When the method
+ * results in an animation needing to be run, the transition will construct
+ * the appropriate {@link Animator} object and return it. The transition
+ * mechanism will apply any applicable duration, startDelay, and interpolator
+ * to that animation and start it. Returning null from the method tells the
+ * transition engine that there is no animation to be played (TransitionGroup
+ * will return null because any applicable animations were started on its child
+ * transitions already and there is no animation to be run on the group itself).
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param endValues
+ * @return Animator The animation to run.
+ */
+ protected abstract Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues);
+
+ /**
+ * 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 things up at the start of a
+ * Transition. For example, if an overall Transition consists 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 scene 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 in the preplay() 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>false</code> indicates that
+ * the transition should not run, and there will be no ensuing call to the
+ * {@link #play(ViewGroup, TransitionValues, TransitionValues)} method during
+ * this scene change. The default implementation returns true.</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 True if the Transition's {@link #play(ViewGroup,
+ * TransitionValues, TransitionValues) play()} method should be called
+ * during this scene change, false otherwise.
+ */
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ return true;
+ }
+
+ /**
+ * This version of prePlay() is called with the entire set of start/end
+ * values. The implementation in Transition iterates through these lists
+ * and calls {@link #prePlay(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. The intention in splitting
+ * preplay() out from play() is to allow all Transitions in the tree to
+ * set up the appropriate start scene for their target objects prior to
+ * any calls to play(), which is necessary when there is a sequential
+ * Transition, where a child transition which is not the first may want to
+ * set up a target's scene prior to the overall Transition start.
+ *
+ * @hide
+ */
+ protected void prePlay(ViewGroup sceneRoot, HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ HashMap<View, TransitionValues> endCopy = new HashMap<View, TransitionValues>(endValues);
+ SparseArray<TransitionValues> endIdCopy =
+ new SparseArray<TransitionValues>(endIdValues.size());
+ for (int i = 0; i < endIdValues.size(); ++i) {
+ int id = endIdValues.keyAt(i);
+ endIdCopy.put(id, endIdValues.valueAt(i));
+ }
+ LongSparseArray<TransitionValues> endItemIdCopy =
+ new LongSparseArray<TransitionValues>(endItemIdValues.size());
+ for (int i = 0; i < endItemIdValues.size(); ++i) {
+ long id = endItemIdValues.keyAt(i);
+ endItemIdCopy.put(id, endItemIdValues.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.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.get(view) != null ?
+ startValues.get(view) : startIdValues.get(id);
+ if (endValues.get(view) != null) {
+ end = endValues.get(view);
+ endCopy.remove(view);
+ } else {
+ end = endIdValues.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 = startItemIdValues.get(itemId);
+ endItemIdCopy.remove(itemId);
+ // TODO: deal with targetIDs for itemIDs for ListView items
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ }
+ int startItemIdCopySize = startItemIdValues.size();
+ for (int i = 0; i < startItemIdCopySize; ++i) {
+ long id = startItemIdValues.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startItemIdValues.get(id);
+ TransitionValues end = endItemIdValues.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.get(view) != null ?
+ startValues.get(view) : startIdValues.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 = startIdValues.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 = startItemIdValues.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?
+ if (prePlay(sceneRoot, start, end)) {
+ // Note: we've already done the check against targetIDs in these lists
+ mPlayStartValuesList.add(start);
+ mPlayEndValuesList.add(end);
+ }
+ }
+ }
+
+ /**
+ * 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 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,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+
+ 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();
+ animate(play(sceneRoot, start, end));
+ }
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ endTransition();
+ }
+
+ /**
+ * 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 preplay and play() methods to determine what, if any,
+ * animations, should be run.
+ *
+ * @param transitionValues The holder any values that the Transition
+ * wishes to store. Values are stored in the fields of this
+ * TransitionValues object, according to their type, and are keyed from
+ * a String value. For example, to start a view's rotation value,
+ * a Transition might call
+ * <code>transitionValues.floatValues.put("rotation", view.getRotation())
+ * </code>. The target <code>View</code> will already be stored in
+ * the transitionValues structure when this method is called. The other
+ * fields in TransitionValues, e.g. <code>floatValues</code>,
+ * may need to be instantiated if they have not yet been created.
+ */
+ 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.put(view, values);
+ mStartIdValues.put(id, values);
+ } else {
+ mEndValues.put(view, values);
+ mEndIdValues.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.put(view, values);
+ } else {
+ mEndValues.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.put(view, values);
+ mStartIdValues.put((int) id, values);
+ } else {
+ mStartItemIdValues.put(id, values);
+ }
+ } else {
+ if (!isListViewItem) {
+ mEndValues.put(view, values);
+ mEndIdValues.put((int) id, values);
+ } else {
+ mEndItemIdValues.put(id, values);
+ }
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) view;
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ captureHierarchy(parent.getChildAt(i), start);
+ }
+ }
+ }
+
+ /**
+ * Called by TransitionManager to play the transition. This calls
+ * prePlay() and then play() with the full set of per-view
+ * transitionValues objects
+ */
+ void play(ViewGroup sceneRoot) {
+ // prePlay() 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
+ prePlay(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ play(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ }
+
+ /**
+ * 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 onAnimationCancel(Animator animation) {
+ cancelTransition();
+ }
+
+ @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 < mStartItemIdValues.size(); ++i) {
+ TransitionValues tv = mStartItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ for (int i = 0; i < mEndItemIdValues.size(); ++i) {
+ TransitionValues tv = mEndItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ mStartValues.clear();
+ mStartIdValues.clear();
+ mStartItemIdValues.clear();
+ mEndValues.clear();
+ mEndIdValues.clear();
+ mEndItemIdValues.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
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ 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..363872a
--- /dev/null
+++ b/core/java/android/view/transition/TransitionGroup.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.transition;
+
+import android.animation.Animator;
+import android.util.AndroidRuntimeException;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * 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]);
+ }
+ }
+ }
+
+ /**
+ * Removes the specified child transition from this group.
+ *
+ * @param transition The transition to be removed.
+ */
+ public void removeTransition(Transition transition) {
+ mTransitions.remove(transition);
+ }
+
+ /**
+ * 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() {
+ for (Transition childTransition : mTransitions) {
+ childTransition.addListener(mListener);
+ }
+ mCurrentListeners = mTransitions.size();
+ }
+
+ /**
+ * This listener is used to detect when all child transitions are done, at
+ * which point this transition group is also done.
+ */
+ private TransitionListener mListener = new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (!mStarted) {
+ startTransition();
+ mStarted = true;
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ --mCurrentListeners;
+ if (mCurrentListeners == 0) {
+ // All child trans
+ mStarted = false;
+ endTransition();
+ }
+ transition.removeListener(this);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void prePlay(ViewGroup sceneRoot,
+ HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ for (Transition childTransition : mTransitions) {
+ childTransition.prePlay(sceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void play(ViewGroup sceneRoot,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+ setupStartEndListeners();
+ final ViewGroup finalSceneRoot = sceneRoot;
+ 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.play(finalSceneRoot,
+ startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ transition.removeListener(this);
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot,
+ TransitionValues startValues, TransitionValues endValues) {
+ final View view = (endValues != null) ? endValues.view :
+ (startValues != null) ? startValues.view : null;
+ final int targetId = (view != null) ? view.getId() : -1;
+ // TODO: not sure this is a valid check - what about auto-targets? No need for ids.
+ if (targetId < 0) {
+ return null;
+ }
+ setupStartEndListeners();
+ if (!mPlayTogether) {
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final TransitionValues finalStartValues = startValues;
+ final TransitionValues finalEndValues = endValues;
+ // 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.startTransition();
+ if (nextTransition.isValidTarget(view, targetId)) {
+ animate(nextTransition.play(finalSceneRoot, finalStartValues,
+ finalEndValues));
+ } else {
+ nextTransition.endTransition();
+ }
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.startTransition();
+ if (firstTransition.isValidTarget(view, targetId)) {
+ animate(firstTransition.play(finalSceneRoot, finalStartValues, finalEndValues));
+ } else {
+ firstTransition.endTransition();
+ }
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.startTransition();
+ if (childTransition.isValidTarget(view, targetId)) {
+ animate(childTransition.play(sceneRoot, startValues, endValues));
+ } else {
+ childTransition.endTransition();
+ }
+ }
+ }
+ return null;
+ }
+
+ @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
+ 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;
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionInflater.java b/core/java/android/view/transition/TransitionInflater.java
new file mode 100644
index 0000000..a5f5836
--- /dev/null
+++ b/core/java/android/view/transition/TransitionInflater.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.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;
+import java.util.HashMap;
+
+/**
+ * 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 HashMap<Context, TransitionInflater> sInflaterMap =
+ new HashMap<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..5a1991c
--- /dev/null
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.HashMap;
+
+/**
+ * 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();
+
+ HashMap<Scene, Transition> mSceneTransitions = new HashMap<Scene, Transition>();
+ HashMap<Scene, HashMap<Scene, Transition>> mScenePairTransitions =
+ new HashMap<Scene, HashMap<Scene, Transition>>();
+
+ /**
+ * 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) {
+ HashMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+ if (sceneTransitionMap == null) {
+ sceneTransitionMap = new HashMap<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) {
+ HashMap<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, final Transition transition) {
+
+ final ViewGroup sceneRoot = scene.getSceneRoot();
+
+ // 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();
+ }
+
+ scene.enter();
+
+ if (transition != null) {
+ final ViewTreeObserver observer = sceneRoot.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ transition.captureValues(sceneRoot, false);
+ transition.play(sceneRoot);
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * 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);
+ }
+}
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/view/transition/TransitionValues.java
new file mode 100644
index 0000000..120ace8
--- /dev/null
+++ b/core/java/android/view/transition/TransitionValues.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 android.view.transition;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * 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 hashmap 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 during the play phase of the scene change,
+ * for {@link Transition#prePlay(ViewGroup, TransitionValues, TransitionValues)} and
+ * for {@link Transition#play(ViewGroup, TransitionValues, TransitionValues)}.</p>
+ */
+public class TransitionValues {
+
+ /**
+ * The View with these values
+ */
+ public View view;
+
+ /**
+ * The set of values tracked by transitions for this scene
+ */
+ public final HashMap<String, Object> values = new HashMap<String, Object>();
+
+ @Override
+ public String toString() {
+ String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
+ returnValue += " view = " + view + "\n";
+ returnValue += " values = " + values + "\n";
+ return returnValue;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Visibility.java b/core/java/android/view/transition/Visibility.java
new file mode 100644
index 0000000..a3e6e77
--- /dev/null
+++ b/core/java/android/view/transition/Visibility.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * 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 #preAppear(ViewGroup, View, int, View, int)},
+ * {@link #preDisappear(ViewGroup, View, int, View, int)},
+ * {@link #appear(ViewGroup, View, int, View, int)}, and
+ * {@link #disappear(ViewGroup, View, int, View, 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";
+
+ @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());
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return false;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return preAppear(sceneRoot, existenceChange ? null : startValues.view,
+ startVisibility, endValues.view, endVisibility);
+ } else {
+ return preDisappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, endVisibility);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return null;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return appear(sceneRoot, existenceChange ? null : startValues.view, startVisibility,
+ endValues.view, endVisibility);
+ } else {
+ return disappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, 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 startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * 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 startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * appear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) { return null; }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * disappear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, 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/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index c72853f..2012988 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2398,7 +2398,7 @@
* @return True if the selector should be shown
*/
boolean shouldShowSelector() {
- return (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState();
+ return (!isInTouchMode()) || touchModeDrawsInPressedState();
}
private void drawSelector(Canvas canvas) {
@@ -2736,7 +2736,7 @@
}
public boolean sameWindow() {
- return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
+ return getWindowAttachCount() == mOriginalAttachCount;
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f57f333..91c989d 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();
}
}
@@ -1082,9 +1097,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 +1223,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 +1261,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 +1887,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 +1906,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 +1941,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 +1972,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 +1982,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 +1998,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 +2026,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 +3971,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/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/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/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 9e3f87f..8d90973 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;
@@ -284,6 +285,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 {
@@ -1503,6 +1511,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.
*
@@ -4394,16 +4443,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);
}
/**
@@ -5268,7 +5331,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);
}
@@ -5366,6 +5428,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()) {
@@ -5474,12 +5545,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;
}
/**
@@ -5512,6 +5587,10 @@
return super.onKeyUp(keyCode, event);
}
+ if (!KeyEvent.isModifierKey(keyCode)) {
+ mPreventDefaultMovement = false;
+ }
+
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.hasNoModifiers()) {
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/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 04b9884..33ce573 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -83,7 +83,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 = 65 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -663,7 +663,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
@@ -1048,7 +1048,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);
}
@@ -2240,6 +2240,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);
}
@@ -2541,6 +2549,8 @@
boolean mVideoTurnedOn;
StopwatchTimer mVideoTurnedOnTimer;
+ StopwatchTimer mForegroundActivityTimer;
+
BatchTimer mVibratorOnTimer;
Counter[] mUserActivityCounters;
@@ -2776,6 +2786,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,
@@ -2844,6 +2875,11 @@
}
@Override
+ public Timer getForegroundActivityTimer() {
+ return mForegroundActivityTimer;
+ }
+
+ @Override
public Timer getVibratorOnTimer() {
return mVibratorOnTimer;
}
@@ -2919,6 +2955,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();
@@ -3018,6 +3057,10 @@
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();
@@ -3099,6 +3142,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);
@@ -3204,6 +3253,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 {
@@ -3372,16 +3427,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;
@@ -3392,16 +3447,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;
@@ -3412,16 +3467,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;
@@ -3432,15 +3487,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;
@@ -3453,8 +3508,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) {
@@ -5362,6 +5417,9 @@
u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
+ u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
+ }
+ if (in.readInt() != 0) {
u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
}
@@ -5415,6 +5473,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) {
@@ -5564,6 +5623,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);
@@ -5633,6 +5698,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);
@@ -5908,7 +5974,7 @@
updateKernelWakelocksLocked();
}
- public void dumpLocked(PrintWriter pw) {
+ public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly) {
if (DEBUG) {
Printer pr = new PrintWriterPrinter(pw);
pr.println("*** Screen timer:");
@@ -5940,7 +6006,7 @@
pr.println("*** Mobile ifaces:");
pr.println(mMobileIfaces.toString());
}
- super.dumpLocked(pw);
+ super.dumpLocked(pw, isUnpluggedOnly);
}
private NetworkStats mNetworkSummaryCache;
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index b1bb8c1..bd0914d 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -518,6 +518,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 +535,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) {
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..880464d
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardService.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 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 onWakeKeyWhenKeyguardShowing(int keyCode);
+ oneway void onWakeMotionWhenKeyguardShowing();
+ 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/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/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/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/jni/Android.mk b/core/jni/Android.mk
index 594d578..805f457 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -51,6 +51,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 \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ca658da..bfa2e06 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>
@@ -117,6 +116,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);
@@ -1112,6 +1112,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),
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index d4c7600..191b54a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -398,7 +398,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 +423,6 @@
env->DeleteGlobalRef(fStorageObj);
}
fStorageObj = NULL;
-
- // Set this to NULL to prevent the SkMallocPixelRef destructor
- // from freeing the memory.
- fStorage = NULL;
}
}
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/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index b2cf9c1..be14f58 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>
@@ -839,7 +838,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_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_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_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_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b87fe27..cd7a032 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
// ----------------------------------------------------------------------------
@@ -350,31 +377,20 @@
// ----------------------------------------------------------------------------
static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
- jfloat left, jfloat top, SkPaint* paint) {
- // This object allows the renderer to allocate a global JNI ref to the buffer object.
- JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
-
+ OpenGLRenderer* renderer, SkBitmap* bitmap, jfloat left, jfloat top, SkPaint* paint) {
renderer->drawBitmap(bitmap, left, top, paint);
}
static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
+ OpenGLRenderer* renderer, SkBitmap* bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) {
- // This object allows the renderer to allocate a global JNI ref to the buffer object.
- JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
-
renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint);
}
static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject clazz,
- 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);
-
+ OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
renderer->drawBitmap(bitmap, matrix, paint);
}
@@ -404,12 +420,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) {
- // This object allows the renderer to allocate a global JNI ref to the buffer object.
- JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
-
+ OpenGLRenderer* renderer, SkBitmap* bitmap, jint meshWidth, jint meshHeight,
+ jfloatArray vertices, jint offset, jintArray colors, jint colorOffset, SkPaint* paint) {
jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
@@ -420,18 +432,13 @@
}
static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, jbyteArray chunks,
+ OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks,
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);
+ renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
env->ReleaseByteArrayElements(chunks, storage, 0);
}
@@ -932,9 +939,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 +954,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 },
@@ -977,14 +990,14 @@
{ "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
{ "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
- { "nDrawBitmap", "(II[BFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
- { "nDrawBitmap", "(II[BFFFFFFFFI)V",(void*) android_view_GLES20Canvas_drawBitmapRect },
- { "nDrawBitmap", "(II[BII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+ { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
+ { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+ { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
{ "nDrawBitmap", "(I[IIIFFIIZI)V", (void*) android_view_GLES20Canvas_drawBitmapData },
- { "nDrawBitmapMesh", "(II[BII[FI[III)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
+ { "nDrawBitmapMesh", "(IIII[FI[III)V", (void*) android_view_GLES20Canvas_drawBitmapMesh },
- { "nDrawPatch", "(II[B[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+ { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
{ "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
{ "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
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_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/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 d2895b7..8ef127a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1078,6 +1078,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 -->
<!-- ================================== -->
@@ -2202,6 +2210,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. -->
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/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-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-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/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/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3251313..bc23476 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b56bca2..602120c 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index f101f16..9d038a3 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 1f1093b..fef9e7e 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6774709..cfd8b2d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9900c69c..83ca3ed 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9b18284..79f8986 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 54d61a4..07838fe 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac82f62..61a88a3 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2cb7381..f3cd839 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 555d209..a3f2a55 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ad4776d..072f98b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c8665cd..5b8a35b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 354597b..09dd5a6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 6bf84b6..bce35c3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4ddd489f..832536b 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b58c3f3..c28ca11 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index df153cb..7886ea0 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c2f6acf..fe38f30 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 38ed2ad..e8af306 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 986a001..67fb775 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b0512da..cbce892 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 0b818d7..8213e24 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e1320d8b..3bdd67d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 6e280b6..87e73d1 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
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 1136b7f..6fb098c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 1efb916..15116e3 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 020bce2..e4c0814 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -615,6 +615,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>
@@ -1257,6 +1259,12 @@
<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>
+ <!-- no translation found for permlab_control_keyguard (172195184207828387) -->
+ <skip />
+ <!-- no translation found for permdesc_control_keyguard (3043732290518629061) -->
+ <skip />
<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>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 8c8deb2..4b31a24 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 38abeda..dcec340 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b529eca..3bceb24 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
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 8335f4c..50b1571 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a69e588..fe22ca9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index bfa38e0..54e9bf2 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1025,6 +1025,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) -->
@@ -1981,6 +1985,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) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7369ae8..f3ad580 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 5c3c0b7..c823875 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 06461f2..bb4df56 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7b894e2..670cc61 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 82c982d..d40c581 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cfeef37..567df77 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6862ed5..66e0d6c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
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 c7f025c..8145a4d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index b594c58..c0e163f 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a8d374b..2cdafb6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index ecb0c30..3ed8763 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ac75d9d8..6067f1e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 140dbfb..7e7513b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index aaa4c2f..8f899eb 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f69f44a..57d943a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -613,6 +613,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>
@@ -1255,6 +1257,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>
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..5d71f75 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4408,6 +4408,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 f2c0aa0..ad7c51a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1746,4 +1746,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/public.xml b/core/res/res/values/public.xml
index 2d97138..089a859 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2052,4 +2052,15 @@
<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" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a1479af..50fad5f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1797,6 +1797,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 +3474,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>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6a3bdaa..1cf4538 100644
--- 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" />
@@ -572,26 +568,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 +577,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" />
@@ -1022,16 +995,11 @@
<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" />
@@ -1131,10 +1099,7 @@
<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" />
@@ -1152,7 +1117,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" />
@@ -1206,8 +1170,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" />
@@ -1216,22 +1178,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" />
@@ -1246,33 +1198,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" />
@@ -1291,9 +1221,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" />
@@ -1301,15 +1229,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" />
@@ -1317,57 +1242,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" />
@@ -1375,18 +1257,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" />
@@ -1395,9 +1267,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" />
@@ -1413,41 +1282,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" />
@@ -1455,40 +1290,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" />
@@ -1715,6 +1516,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/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..d26b5a2 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -45,12 +45,9 @@
/**
* Backing buffer for the Bitmap.
- * Made public for quick access from drawing methods -- do NOT modify
- * from outside this class.
- *
- * @hide
*/
- public byte[] mBuffer;
+ @SuppressWarnings("UnusedDeclaration") // native code only
+ private byte[] mBuffer;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
private final BitmapFinalizer mFinalizer;
@@ -701,12 +698,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;
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index c851844..bbba778 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1064,14 +1064,13 @@
*
* 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, RectF dst, Paint paint) {
}
/**
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 6de4d84..81e9d84 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -36,7 +36,10 @@
*/
public class NinePatch {
private final Bitmap mBitmap;
- private final byte[] mChunk;
+ /**
+ * @hide
+ */
+ public final byte[] mChunk;
private Paint mPaint;
private String mSrcName; // Useful for debugging
private final RectF mRect = new RectF();
@@ -72,6 +75,13 @@
public void setPaint(Paint p) {
mPaint = p;
}
+
+ /**
+ * @hide
+ */
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
/**
* Draw a bitmap of nine patches.
@@ -86,7 +96,7 @@
mPaint != null ? mPaint.mNativePaint : 0,
canvas.mDensity, mBitmap.mDensity);
} else {
- canvas.drawPatch(mBitmap, mChunk, location, mPaint);
+ canvas.drawPatch(this, location, mPaint);
}
}
@@ -104,7 +114,7 @@
canvas.mDensity, mBitmap.mDensity);
} else {
mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
+ canvas.drawPatch(this, mRect, mPaint);
}
}
@@ -122,7 +132,7 @@
canvas.mDensity, mBitmap.mDensity);
} else {
mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, paint);
+ canvas.drawPatch(this, mRect, paint);
}
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 34f9070..9042ce6 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/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 b7429d4..dcfe20f 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..6d236d9 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.
*/
@@ -985,6 +996,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 8a4d598..40089be 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -114,6 +114,11 @@
}
@Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
public void setDither(boolean dither) {
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
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 231234c..1507a9b 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 dd692c6..dfbd986 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/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index a9dc22b..b83815b 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -251,6 +251,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.
@@ -394,7 +403,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;
@@ -430,6 +439,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 e987679..7cbc737 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 bd2b2f0..8a28b2b 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/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 4493d41..8717638 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -29,6 +29,7 @@
import android.os.Process;
import android.util.Log;
import android.view.Surface;
+import android.os.SystemProperties;
@@ -56,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);
+ }
}
}
@@ -93,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;
}
@@ -1135,6 +1143,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/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/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 5b45d70..ccccc2e 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1553,7 +1553,7 @@
static bool getIdmapInfo(const void* idmap, size_t size,
uint32_t* pOriginalCrc, uint32_t* pOverlayCrc);
-#ifndef HAVE_ANDROID_OS
+#ifdef STATIC_ANDROIDFW_FOR_TOOLS
void print(bool inclValues) const;
static String8 normalizeForOutput(const char* input);
#endif
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..bb79f27 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -22,9 +22,13 @@
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:= \
@@ -52,6 +56,8 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+
LOCAL_C_INCLUDES := \
external/zlib
@@ -92,6 +98,7 @@
ifeq ($(TARGET_OS),linux)
include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
LOCAL_C_INCLUDES += \
external/skia/include/core \
external/zlib \
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/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a730065..e31246f 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,7 +5323,7 @@
}
-#ifndef HAVE_ANDROID_OS
+#ifdef STATIC_ANDROIDFW_FOR_TOOLS
#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
#define CHAR16_ARRAY_EQ(constant, var, len) \
@@ -5621,6 +5620,6 @@
}
}
-#endif // HAVE_ANDROID_OS
+#endif // STATIC_ANDROIDFW_FOR_TOOLS
} // 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..6be7c5b 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -7,7 +7,8 @@
InputChannel_test.cpp \
InputEvent_test.cpp \
InputPublisherAndConsumer_test.cpp \
- ObbFile_test.cpp
+ ObbFile_test.cpp \
+ ZipFileRO_test.cpp
shared_libraries := \
libandroidfw \
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..771ac45 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 \
@@ -52,17 +54,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..4d2fc014
--- /dev/null
+++ b/libs/hwui/AssetAtlas.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "AssetAtlas.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);
+ mTexture = mImage->getTexture();
+
+ if (mTexture) {
+ mWidth = buffer->getWidth();
+ mHeight = buffer->getHeight();
+
+ createEntries(map, count);
+ } else {
+ ALOGW("Could not create atlas image");
+
+ delete mImage;
+ mImage = NULL;
+ }
+}
+
+void AssetAtlas::terminate() {
+ if (mImage) {
+ delete mImage;
+ mImage = NULL;
+
+ for (size_t i = 0; i < mEntries.size(); i++) {
+ delete mEntries.valueAt(i);
+ }
+ mEntries.clear();
+
+ mWidth = mHeight = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+}
+
+/**
+ * TODO: This method does not take the rotation flag into account
+ */
+void AssetAtlas::createEntries(int* map, int count) {
+ 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 / (float) mWidth, (x + bitmap->width()) / (float) mWidth,
+ y / (float) mHeight, (y + bitmap->height()) / (float) mHeight);
+
+ Entry* entry = new Entry(bitmap, x, y, rotated, mapper, *this);
+ entry->texture.id = mTexture;
+ entry->texture.blend = !bitmap->isOpaque();
+ entry->texture.width = bitmap->width();
+ entry->texture.height = bitmap->height();
+ entry->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..0bbd2a7
--- /dev/null
+++ b/libs/hwui/AssetAtlas.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 {
+
+/**
+ * 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;
+
+ /**
+ * 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;
+
+ /*
+ * A "virtual texture" object that represents the texture
+ * this entry belongs to. This texture should never be
+ * modified.
+ */
+ Texture texture;
+
+ private:
+ Entry(SkBitmap* bitmap, int x, int y, bool rotated,
+ const UvMapper& mapper, const AssetAtlas& atlas):
+ bitmap(bitmap), x(x), y(y), rotated(rotated), uvMapper(mapper), atlas(atlas) { }
+
+ friend class AssetAtlas;
+ };
+
+ AssetAtlas(): mWidth(0), mHeight(0), mTexture(0), mImage(NULL) { }
+ ~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 mWidth;
+ }
+
+ /**
+ * Returns the height of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getHeight() const {
+ return mHeight;
+ }
+
+ /**
+ * Returns the OpenGL name of the texture backing this atlas.
+ * Can return 0 if the atlas is not initialized.
+ */
+ GLuint getTexture() const {
+ return mTexture;
+ }
+
+ /**
+ * 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;
+
+private:
+ void createEntries(int* map, int count);
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+
+ GLuint mTexture;
+ Image* mImage;
+
+ 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..f08b5ca 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -58,8 +58,8 @@
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 +82,7 @@
mTextureUnit = 0;
mRegionMesh = NULL;
+ mMeshIndices = 0;
blend = false;
lastSrcMode = GL_ZERO;
@@ -94,7 +95,11 @@
debugOverdraw = false;
debugStencilClip = kStencilHide;
+ patchCache.init(*this);
+
mInitialized = true;
+
+ return true;
}
void Caches::initFont() {
@@ -147,7 +152,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 +196,9 @@
glDeleteBuffers(1, &meshBuffer);
mCurrentBuffer = 0;
- glDeleteBuffers(1, &mRegionMeshIndices);
+ glDeleteBuffers(1, &mMeshIndices);
delete[] mRegionMesh;
+ mMeshIndices = 0;
mRegionMesh = NULL;
fboCache.clear();
@@ -200,6 +206,10 @@
programCache.clear();
currentProgram = NULL;
+ assetAtlas.terminate();
+
+ patchCache.clear();
+
mInitialized = false;
}
@@ -227,6 +237,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 +246,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 +254,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);
}
@@ -357,6 +368,32 @@
return false;
}
+bool Caches::bindIndicesBuffer() {
+ if (!mMeshIndices) {
+ 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, &mMeshIndices);
+ bool force = bindIndicesBuffer(mMeshIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 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);
@@ -546,27 +583,6 @@
// 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);
}
return mRegionMesh;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 91b938b..18aeeab 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"
@@ -113,7 +118,7 @@
/**
* Initialize caches.
*/
- void init();
+ bool init();
/**
* Initialize global system properties.
@@ -172,6 +177,11 @@
*/
bool unbindMeshBuffer();
+ /**
+ * Binds a global indices buffer that can draw up to
+ * REGION_MESH_QUAD_COUNT quads.
+ */
+ bool bindIndicesBuffer();
bool bindIndicesBuffer(const GLuint buffer);
bool unbindIndicesBuffer();
@@ -290,6 +300,8 @@
Dither dither;
Stencil stencil;
+ AssetAtlas assetAtlas;
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -336,7 +348,9 @@
// Used to render layers
TextureVertex* mRegionMesh;
- GLuint mRegionMeshIndices;
+
+ // Global index buffer
+ GLuint mMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
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/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a0290e3..990372e 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -26,8 +26,10 @@
#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 { \
@@ -721,7 +723,6 @@
int mSetBits;
};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -729,9 +730,16 @@
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), mAtlasEntry(NULL) {
+ }
+
+ DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint,
+ const AssetAtlas::Entry* entry)
+ : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint),
+ mBitmap(bitmap), mAtlasEntry(entry) {
+ if (entry) mUvMapper = entry->uvMapper;
+ }
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
@@ -749,14 +757,14 @@
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 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;
+
+ 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);
@@ -777,7 +785,7 @@
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
- *mergeId = (mergeid_t)mBitmap;
+ *mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap;
// don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
// MergingDrawBatch::canMergeWith
@@ -787,6 +795,8 @@
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
+ const AssetAtlas::Entry* mAtlasEntry;
+ UvMapper mUvMapper;
};
class DrawBitmapMatrixOp : public DrawBoundedOp {
@@ -904,20 +914,16 @@
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)
+ DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
+ 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) {};
+ mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode) {
+ mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap);
+ };
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,
+ return renderer.drawPatch(mBitmap, mPatch, mEntry, mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
}
@@ -929,20 +935,16 @@
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Patch;
- *mergeId = (mergeid_t)mBitmap;
+ *mergeId = (mergeid_t) mBitmap;
return true;
}
private:
SkBitmap* mBitmap;
- const int32_t* mxDivs;
- const int32_t* myDivs;
- const uint32_t* mColors;
- uint32_t mxDivsCount;
- uint32_t myDivsCount;
- int8_t mNumColors;
+ Res_png_9patch* mPatch;
int mAlpha;
SkXfermode::Mode mMode;
+ AssetAtlas::Entry* mEntry;
};
class DrawColorOp : public DrawOp {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 876c38a..bfd4086 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -257,7 +257,8 @@
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint));
+ const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint, entry));
return DrawGlInfo::kStatusDone;
}
@@ -281,7 +282,8 @@
(srcBottom - srcTop == dstBottom - dstTop) &&
(srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
+ const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint, entry));
return DrawGlInfo::kStatusDone;
}
@@ -313,20 +315,15 @@
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) {
+status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint) {
int alpha;
SkXfermode::Mode mode;
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
bitmap = refBitmap(bitmap);
- xDivs = refBuffer<int>(xDivs, width);
- yDivs = refBuffer<int>(yDivs, height);
- colors = refBuffer<uint32_t>(colors, numColors);
- 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, alpha, mode));
return DrawGlInfo::kStatusDone;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 75abad6..db08921 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);
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 54a3987..a3f7c44 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -33,9 +33,6 @@
class 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; }
@@ -53,6 +50,9 @@
void dump() const;
private:
+ Extensions();
+ ~Extensions();
+
friend class Singleton<Extensions>;
SortedVector<String8> mExtensionList;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 543cfa2..a9bf13e 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"
@@ -532,13 +534,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 +640,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..cbbd871 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;
@@ -168,10 +171,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/Image.cpp b/libs/hwui/Image.cpp
new file mode 100644
index 0000000..35ca40d
--- /dev/null
+++ b/libs/hwui/Image.cpp
@@ -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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.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);
+ glBindTexture(GL_TEXTURE_2D, 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;
+
+ glDeleteTextures(1, &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/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ddb190e..f220e4f 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>
@@ -120,6 +119,7 @@
mFirstSnapshot = new Snapshot;
mFrameStarted = false;
+ mCountOverdraw = false;
mScissorOptimizationDisabled = false;
}
@@ -222,6 +222,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 +254,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 +336,10 @@
#endif
}
+ if (mCountOverdraw) {
+ countOverdraw();
+ }
+
mFrameStarted = false;
}
@@ -459,7 +464,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 +476,7 @@
}
resume();
- return result;
+ return result | DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
@@ -524,6 +529,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
///////////////////////////////////////////////////////////////////////////////
@@ -1656,6 +1676,8 @@
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
mCaches.stencil.isTestEnabled();
+
+ mDescription.emulateStencil = mCountOverdraw;
}
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1873,7 +1895,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();
@@ -1904,8 +1926,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);
@@ -1980,9 +2012,11 @@
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,
@@ -1992,8 +2026,9 @@
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
+
const AutoTexture autoCleanup(texture);
int alpha;
@@ -2030,7 +2065,7 @@
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2053,7 +2088,7 @@
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2116,6 +2151,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;
@@ -2125,6 +2164,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;
@@ -2154,11 +2195,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);
@@ -2211,17 +2253,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);
@@ -2292,34 +2336,32 @@
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,
+ return drawPatch(bitmap, patch, mCaches.assetAtlas.getEntry(bitmap),
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) {
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ AssetAtlas::Entry* entry, 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;
-
- const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
+ const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
+ right - left, bottom - top, patch);
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);
@@ -2342,19 +2384,23 @@
}
}
+ alpha *= mSnapshot->alpha;
+
if (CC_LIKELY(pureTranslate)) {
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);
}
}
@@ -3196,6 +3242,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)) {
@@ -3389,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);
}
}
@@ -3438,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,
@@ -3471,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..df275d7 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 {
@@ -78,7 +81,8 @@
};
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;
@@ -188,6 +192,14 @@
*/
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);
@@ -248,11 +260,9 @@
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);
- 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 drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, AssetAtlas::Entry* entry,
float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
@@ -798,6 +808,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,
@@ -943,7 +959,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 +989,7 @@
void debugOverdraw(bool enable, bool clear);
void renderOverdraw();
+ void countOverdraw();
/**
* Should be invoked every time the glScissor is modified.
@@ -985,6 +1002,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 +1038,9 @@
// Used to draw textured quads
TextureVertex mMeshVertices[4];
+ // Default UV mapper
+ const UvMapper mUvMapper;
+
// shader, filters, and shadow
DrawModifiers mDrawModifiers;
SkPaint mFilteredPaint;
@@ -1050,6 +1081,12 @@
// 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;
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 45c619e..6b0734a 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(): 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 left, float top, float right, float bottom, const Res_png_9patch* patch) {
+ UvMapper mapper;
+ return createMesh(bitmapWidth, bitmapHeight, left, top, right, bottom, 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 left, float top, float right, float bottom,
+ const UvMapper& mapper, const Res_png_9patch* patch) {
+
+ 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* vertices = new TextureVertex[maxVertices];
+ TextureVertex* vertex = vertices;
+
+ 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,8 +96,8 @@
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;
@@ -136,8 +108,8 @@
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;
@@ -146,7 +118,6 @@
rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 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,
+ right - left, bitmapWidth, quadCount);
}
y1 = y2;
@@ -189,33 +155,16 @@
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);
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
+ right - left, 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();
- }
-
- 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 +172,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 +187,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 +198,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 +228,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..448cf60 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -23,62 +23,52 @@
#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;
uint32_t verticesCount;
+ uint32_t indexCount;
bool hasEmptyQuads;
Vector<Rect> quads;
+ GLintptr offset;
+ GLintptr textureOffset;
+
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom,
+ const Res_png_9patch* patch);
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom,
+ 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..c2b28d1 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,107 +30,106 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) {
-}
-
-PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) {
+PatchCache::PatchCache(): mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity) {
+ 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);
+ }
+ mSize = 0;
}
PatchCache::~PatchCache() {
clear();
}
+void PatchCache::init(Caches& caches) {
+ glGenBuffers(1, &mMeshBuffer);
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
+
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+}
+
///////////////////////////////////////////////////////////////////////////////
// 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);
+ glDeleteBuffers(1, &mMeshBuffer);
+ clearCache();
+ mSize = 0;
+}
+
+void PatchCache::clearCache() {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ delete i.value();
}
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) {
+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) {
- int8_t transparentQuads = 0;
- uint32_t colorKey = 0;
-
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (colors[i] == 0x0) {
- transparentQuads++;
- colorKey |= 0x1 << i;
- }
- }
- }
-
- // If the 9patch is made of only transparent quads
- if (transparentQuads == int8_t((width + 1) * (height + 1))) {
- return NULL;
- }
-
- const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
-
- ssize_t index = mCache.indexOfKey(description);
- Patch* mesh = NULL;
- if (index >= 0) {
- mesh = mCache.valueAt(index);
- }
+ 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) {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ 0.0f, 0.0f, pixelWidth, pixelHeight, entry->uvMapper, patch);
+ } else {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ 0.0f, 0.0f, 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) {
+ Caches& caches = Caches::getInstance();
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
+
+ // TODO: Simply remove the oldest items until we have enough room
+ // This will require to keep a list of free blocks in the VBO
+ uint32_t size = newMesh->getSize();
+ if (mSize + size > mMaxSize) {
+ clearCache();
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ mSize = 0;
+ }
+
+ newMesh->offset = (GLintptr) mSize;
+ newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ mSize += size;
+
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
+
+ delete[] vertices;
+ }
+
+ mCache.put(description, newMesh);
+ return newMesh;
}
return mesh;
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 0822cba..129a0dc 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -17,8 +17,13 @@
#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"
@@ -40,45 +45,47 @@
// 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;
+ }
+
+ GLuint getMeshBuffer() const {
+ return mMeshBuffer;
}
private:
- /**
- * Description of a patch.
- */
+ void clearCache();
+
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;
+
static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
bool operator==(const PatchDescription& other) const {
@@ -99,21 +106,24 @@
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;
+ uint32_t mMaxSize;
+ uint32_t mSize;
+ LruCache<PatchDescription, Patch*> mCache;
+ GLuint mMeshBuffer;
}; // class PatchCache
}; // namespace uirenderer
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 14a2376..c127d68 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, 0);
+ 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..dd1aaa2 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -85,6 +85,7 @@
#define PROGRAM_HAS_COLORS 42
#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
+#define PROGRAM_EMULATE_STENCIL 44
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -163,6 +164,7 @@
float gamma;
bool hasDebugHighlight;
+ bool emulateStencil;
/**
* Resets this description. All fields are reset back to the default
@@ -275,6 +277,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 +433,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..367294c 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -342,6 +342,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";
@@ -603,7 +609,8 @@
// Optimization for common cases
if (!description.isAA && !blendFramebuffer && !description.hasColors &&
description.colorOp == ProgramDescription::kColorNone &&
- !description.isPoint && !description.hasDebugHighlight) {
+ !description.isPoint && !description.hasDebugHighlight &&
+ !description.emulateStencil) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -683,6 +690,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) {
@@ -757,6 +767,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..7c68b5b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -71,9 +71,9 @@
/**
* 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 non-rectangular clipping debugging.
@@ -133,6 +133,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 +179,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 +196,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/Texture.h b/libs/hwui/Texture.h
index 8d88bdc..dd39cae 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -22,6 +22,8 @@
namespace android {
namespace uirenderer {
+class UvMapper;
+
/**
* Represents an OpenGL texture.
*/
@@ -42,6 +44,8 @@
firstWrap = true;
id = 0;
+
+ uvMapper = NULL;
}
void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
@@ -125,6 +129,11 @@
*/
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.
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/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/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 8295c5f..32e5446 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 e237194..33ba229 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;
@@ -115,9 +116,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;
@@ -255,6 +257,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()
@@ -452,6 +471,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);
@@ -775,23 +795,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) {
@@ -814,12 +837,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;
}
@@ -840,6 +864,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;
@@ -905,7 +934,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;
}
@@ -920,7 +949,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
@@ -956,27 +985,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;
@@ -991,7 +1026,7 @@
}
}
- if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
+ if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
mVolumePanel.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
@@ -1249,11 +1284,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) {
@@ -4382,6 +4422,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;
@@ -5182,6 +5227,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 399eb7b..60eaa92 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,20 +632,26 @@
/**
* 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();
}
/**
- * 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);
@@ -639,7 +662,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}
@@ -647,8 +673,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) {
@@ -781,10 +806,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},
@@ -802,8 +830,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}
*/
@@ -833,6 +864,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}
*/
@@ -841,18 +874,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}
*/
@@ -861,14 +904,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;
}
@@ -879,6 +931,7 @@
//--------------------
/**
* Starts playing an AudioTrack.
+ * If track's creation mode is {@link #MODE_STATIC}, you must have called write() prior.
*
* @throws IllegalStateException
*/
@@ -943,7 +996,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) {
@@ -954,11 +1008,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
@@ -995,16 +1051,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.
@@ -1037,8 +1095,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}
*/
@@ -1079,8 +1137,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/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 61c55a5..1a5b3b9 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;
@@ -1162,7 +1163,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);
}
@@ -1182,7 +1184,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/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/NOTICE b/media/libdrm/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/media/libdrm/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-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.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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/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/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/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/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index de77cac..58fc8e8 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..84c5474
--- /dev/null
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<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">
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.OPEN_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.CREATE_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
new file mode 100644
index 0000000..89f6496
--- /dev/null
+++ b/packages/DocumentsUI/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">Documents</string>
+</resources>
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..d43abde
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+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.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class DirectoryFragment extends ListFragment {
+ private DocumentsAdapter mAdapter;
+ private LoaderCallbacks<Cursor> mCallbacks;
+
+ private static final String EXTRA_URI = "uri";
+
+ private static final int LOADER_DOCUMENTS = 2;
+
+ public static void show(FragmentManager fm, Uri uri, CharSequence title) {
+ 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(android.R.id.content, fragment);
+ ft.addToBackStack(title.toString());
+ ft.setBreadCrumbTitle(title);
+ ft.commitAllowingStateLoss();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final Context context = inflater.getContext();
+
+ mAdapter = new DocumentsAdapter(context);
+ setListAdapter(mAdapter);
+
+ mCallbacks = new LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final Uri uri = args.getParcelable(EXTRA_URI);
+ return new CursorLoader(context, uri, null, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+ };
+
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getLoaderManager().destroyLoader(LOADER_DOCUMENTS);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ final Cursor cursor = (Cursor) mAdapter.getItem(position);
+ final String guid = getCursorString(cursor, DocumentColumns.GUID);
+ final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
+
+ if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
+ // Nested directory picked, recurse using new fragment
+ final Uri childContentsUri = DocumentsContract.buildContentsUri(childUri);
+ final String displayName = cursor.getString(
+ cursor.getColumnIndex(DocumentColumns.DISPLAY_NAME));
+ DirectoryFragment.show(getFragmentManager(), childContentsUri, displayName);
+ } else {
+ // Explicit file picked, return
+ ((DocumentsActivity) getActivity()).onDocumentPicked(childUri);
+ }
+ }
+
+ private class DocumentsAdapter extends CursorAdapter {
+ public DocumentsAdapter(Context context) {
+ super(context, null, false);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return LayoutInflater.from(context)
+ .inflate(com.android.internal.R.layout.preference, parent, false);
+ }
+
+ @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);
+
+ icon.setMaxWidth(128);
+ icon.setMaxHeight(128);
+
+ final String guid = getCursorString(cursor, DocumentColumns.GUID);
+ final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
+ final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+ 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.setImageURI(null);
+ }
+
+ title.setText(displayName);
+ summary.setText(mimeType);
+ }
+ }
+
+ private static String getCursorString(Cursor cursor, String columnName) {
+ return cursor.getString(cursor.getColumnIndex(columnName));
+ }
+
+ private 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..196776b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DocumentsActivity extends Activity {
+ private static final String TAG = "Documents";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SourceFragment.show(getFragmentManager());
+ setResult(Activity.RESULT_CANCELED);
+ }
+
+ public void onDocumentPicked(Uri uri) {
+ Log.d(TAG, "onDocumentPicked() " + uri);
+
+ final Intent intent = new Intent();
+ intent.setData(uri);
+
+ intent.addFlags(
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ if (Intent.ACTION_CREATE_DOCUMENT.equals(getIntent().getAction())) {
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ public static class SourceFragment extends ListFragment {
+ private ArrayList<ProviderInfo> mProviders = Lists.newArrayList();
+ private ArrayAdapter<ProviderInfo> mAdapter;
+
+ public static void show(FragmentManager fm) {
+ final SourceFragment fragment = new SourceFragment();
+
+ final FragmentTransaction ft = fm.beginTransaction();
+ ft.replace(android.R.id.content, 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
+ mProviders.clear();
+ final List<ProviderInfo> providers = context.getPackageManager()
+ .queryContentProviders(null, -1, PackageManager.GET_META_DATA);
+ for (ProviderInfo info : providers) {
+ if (info.metaData != null
+ && info.metaData.containsKey(
+ DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
+ mProviders.add(info);
+ }
+ }
+
+ mAdapter = new ArrayAdapter<ProviderInfo>(
+ context, android.R.layout.simple_list_item_1, mProviders);
+ setListAdapter(mAdapter);
+
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ final ProviderInfo info = mAdapter.getItem(position);
+ final Uri uri = DocumentsContract.buildContentsUri(DocumentsContract.buildDocumentUri(
+ info.authority, DocumentsContract.ROOT_GUID));
+ final String displayName = info.name;
+ DirectoryFragment.show(getFragmentManager(), uri, displayName);
+ }
+ }
+}
diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk
new file mode 100644
index 0000000..32752b8
--- /dev/null
+++ b/packages/ExternalStorageProvider/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 := ExternalStorageProvider
+LOCAL_CERTIFICATE := platform
+
+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/Keyguard/Android.mk b/packages/Keyguard/Android.mk
new file mode 100644
index 0000000..bc86a44
--- /dev/null
+++ b/packages/Keyguard/Android.mk
@@ -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.
+#
+
+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_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..7a40a9e7
--- /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.keyguard"
+ 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..00ca0fa
--- /dev/null
+++ b/packages/Keyguard/proguard.flags
@@ -0,0 +1,2 @@
+-keep public class com.android.keyguard.KeyguardService
+
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..272bc9a
--- /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 tableta."</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 tableta <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 tableta 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 tableta <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica de la tableta."</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 tableta 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..a022655
--- /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 kadi ya SIM..."</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..0f214ba
--- /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ı bağlayı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..ed3faea
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java
@@ -0,0 +1,160 @@
+/*
+ * 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();
+ }
+
+ @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) {
+ }
+}
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..bdc25d9
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -0,0 +1,1665 @@
+/*
+ * 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 mPlaybackState;
+ 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));
+ mPlaybackState = dcs.playbackState;
+
+ if (DEBUG) Log.v(TAG, "Initial transport state: "
+ + mTransportState + ", pbstate=" + mPlaybackState);
+ }
+
+ 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;
+ mTransportState = (clearing ? TRANSPORT_GONE : TRANSPORT_INVISIBLE);
+ KeyguardHostView.this.post(mSwitchPageRunnable);
+ }
+ @Override
+ public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
+ mPlaybackState = playbackState;
+ if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
+ if (mTransportState != TRANSPORT_GONE) {
+ mTransportState = (isMusicPlaying(mPlaybackState) ?
+ TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
+ }
+ 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 int children = mAppWidgetContainer.getChildCount();
+ for (int i = 0; i < children; i++) {
+ if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) {
+ return i;
+ }
+ }
+ 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) {
+ }
+ };
+
+ 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(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(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..7ef5b26
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java
@@ -0,0 +1,229 @@
+/*
+ * 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);
+
+ Bitmap icon = mUserManager.getUserIcon(user.id);
+
+ if (icon == null) {
+ if (DEBUG) Log.w(TAG, "Couldn't get user icon for user id " + user.id);
+ icon = BitmapFactory.decodeResource(mContext.getResources(),
+ R.drawable.ic_contact_picture);
+ }
+
+ mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
+ mFrameShadowColor, mShadowRadius, mHighlightColor);
+ 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..f89ad65
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 onWakeKeyWhenKeyguardShowing(int keyCode) {
+ checkPermission();
+ mKeyguardViewMediator.onWakeKeyWhenKeyguardShowing(keyCode);
+ }
+ public void onWakeMotionWhenKeyguardShowing() {
+ checkPermission();
+ mKeyguardViewMediator.onWakeMotionWhenKeyguardShowing();
+ }
+ 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%
rename from media/libdrm/MODULE_LICENSE_APACHE2
rename 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..79d01dd
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -0,0 +1,954 @@
+/*
+ * 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.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;
+
+
+ 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;
+ }
+ }
+ };
+
+ 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));
+ }
+ }
+ };
+
+ /**
+ * 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 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);
+
+ 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..d3a582f
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -0,0 +1,129 @@
+/*
+ * 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 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..200fb3c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -0,0 +1,263 @@
+/*
+ * 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 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/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
new file mode 100644
index 0000000..b6c35bd
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -0,0 +1,460 @@
+/*
+ * 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) {
+ 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 = 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;
+ 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 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();
+ }
+
+ /**
+ * 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/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
new file mode 100644
index 0000000..78ff3a8
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -0,0 +1,1458 @@
+/*
+ * 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 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 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;
+
+ /**
+ * 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);
+ }
+
+ @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 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());
+ }
+
+ 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);
+
+ 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);
+
+ 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 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 #onWakeKeyWhenKeyguardShowing(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(null);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * 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 onWakeKeyWhenKeyguardShowing(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 onWakeMotionWhenKeyguardShowing() {
+ 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) {
+ 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;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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 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() {
+ 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");
+ 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(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;
+ }
+}
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/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..a3f5c24
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -0,0 +1,2564 @@
+/*
+ * 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);
+ 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..d011df4
--- /dev/null
+++ b/packages/Keyguard/test/Android.mk
@@ -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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := KeyguardTest
+
+# Remove this to verify permission checks are working correctly
+LOCAL_CERTIFICATE := platform
+
+# 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/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 659651b..f894068 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -48,7 +48,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;
@@ -978,7 +977,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
@@ -989,22 +988,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);
}
}
@@ -1019,7 +1005,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
@@ -1030,22 +1016,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/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-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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 66080f3..4bb44af 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" />
@@ -71,6 +72,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/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/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c40e26d..48edc73 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -189,7 +189,7 @@
<string name="quick_settings_location_label" msgid="3292451598267467545">"الموقع المستخدم"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"جهاز الوسائط"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات الطوارئ فقط"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات طوارئ فقط"</string>
<string name="quick_settings_settings_label" msgid="5326556592578065401">"الإعدادات"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"الوقت"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"أنا"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8c2dd8e..580bfe7 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -147,7 +147,7 @@
<string name="accessibility_ringer_silent" msgid="9061243307939135383">"Mode silenci."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
- <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Capa de notificació."</string>
+ <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració ràpida."</string>
<string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"Aplicacions recents."</string>
<string name="accessibility_quick_settings_user" msgid="1104846699869476855">"Usuari <xliff:g id="USER">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 890e05e..7e74e40 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -100,12 +100,12 @@
<string name="accessibility_data_two_bars" msgid="6166018492360432091">"Data two bars."</string>
<string name="accessibility_data_three_bars" msgid="9167670452395038520">"Data three bars."</string>
<string name="accessibility_data_signal_full" msgid="2708384608124519369">"Data signal full."</string>
- <string name="accessibility_wifi_off" msgid="3177380296697933627">"Wifi off."</string>
- <string name="accessibility_no_wifi" msgid="1425476551827924474">"Wifi disconnected."</string>
- <string name="accessibility_wifi_one_bar" msgid="7735893178010724377">"Wifi one bar."</string>
- <string name="accessibility_wifi_two_bars" msgid="4994274250497262434">"Wifi two bars."</string>
- <string name="accessibility_wifi_three_bars" msgid="3495755044276588384">"Wifi three bars."</string>
- <string name="accessibility_wifi_signal_full" msgid="6853561303586480376">"Wifi signal full."</string>
+ <string name="accessibility_wifi_off" msgid="3177380296697933627">"Wi-Fi off."</string>
+ <string name="accessibility_no_wifi" msgid="1425476551827924474">"Wi-Fi disconnected."</string>
+ <string name="accessibility_wifi_one_bar" msgid="7735893178010724377">"Wi-Fi one bar."</string>
+ <string name="accessibility_wifi_two_bars" msgid="4994274250497262434">"Wi-Fi two bars."</string>
+ <string name="accessibility_wifi_three_bars" msgid="3495755044276588384">"Wi-Fi three bars."</string>
+ <string name="accessibility_wifi_signal_full" msgid="6853561303586480376">"Wi-Fi signal full."</string>
<string name="accessibility_no_wimax" msgid="4329180129727630368">"No WiMAX."</string>
<string name="accessibility_wimax_one_bar" msgid="4170994299011863648">"WiMAX one bar."</string>
<string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAX two bars."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d619de1..1fe4d46 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -58,7 +58,7 @@
<string name="label_view" msgid="6304565553218192990">"Ver"</string>
<string name="always_use_device" msgid="1450287437017315906">"Se usa de forma predeterminada para este dispositivo USB."</string>
<string name="always_use_accessory" msgid="1210954576979621596">"Se usa de forma predeterminada para este accesorio USB."</string>
- <string name="usb_debugging_title" msgid="4513918393387141949">"¿Permitir depuración de USB?"</string>
+ <string name="usb_debugging_title" msgid="4513918393387141949">"¿Permitir depuración por USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"La huella digital de tu clave RSA es:"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Permitir siempre desde esta computadora"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ocupar la pantalla"</string>
@@ -131,9 +131,9 @@
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"No hay tarjeta SIM."</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"Sin tarjeta SIM"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Conexión mediante Bluetooth"</string>
- <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo de avión"</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
<!-- String.format failed for translation -->
<!-- no translation found for accessibility_battery_level (7451474187113371965) -->
<skip />
@@ -154,7 +154,7 @@
<string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Móvil <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"Batería <xliff:g id="STATE">%s</xliff:g>"</string>
- <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo de avión <xliff:g id="STATE">%s</xliff:g>"</string>
+ <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo avión <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos de 2G-3G inhabilitados"</string>
@@ -178,7 +178,7 @@
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
<string name="start_dreams" msgid="7219575858348719790">"Activar protector"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
- <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avión"</string>
+ <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo avión"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Cargada"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index babbcce..294c0ee 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -185,7 +185,7 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> appareils)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth désactivé"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotation auto."</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotation auto"</string>
<string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation bloquée"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Mode de saisie"</string>
<string name="quick_settings_location_label" msgid="3292451598267467545">"Utilisation des données de localisation"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 26151dd..581c56c 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -42,7 +42,7 @@
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Setelan"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Mode pesawat"</string>
- <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Putar layar secara otomatis"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Rotasi layar otomatis"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"BUNGKAM"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTO"</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Pemberitahuan"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 812d1ac..3d7cb76 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -25,11 +25,11 @@
<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 applicazione recente"</string>
- <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignora applicazioni recenti"</string>
+ <string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Nessuna app recente"</string>
+ <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignora app recenti"</string>
<plurals name="status_bar_accessibility_recent_apps">
- <item quantity="one" msgid="5854176083865845541">"1 applicazione recente"</item>
- <item quantity="other" msgid="1040784359794890744">"Applicazioni recenti in %d"</item>
+ <item quantity="one" msgid="5854176083865845541">"1 app recente"</item>
+ <item quantity="other" msgid="1040784359794890744">"App recenti in %d"</item>
</plurals>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nessuna notifica"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string>
@@ -149,7 +149,7 @@
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifica eliminata."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Area notifiche."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Impostazioni rapide."</string>
- <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"Applicazioni recenti."</string>
+ <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"App recenti"</string>
<string name="accessibility_quick_settings_user" msgid="1104846699869476855">"Utente <xliff:g id="USER">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Cellulare: <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 150375d..56122de 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"המסך נעול כעת לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"המסך נעול כעת לאורך."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"חלום בהקיץ"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"מצב טיסה"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"טוען (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 57e70c8..372389f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -65,7 +65,7 @@
<string name="compat_mode_off" msgid="4434467572461327898">"Stiepiet, lai aizp. ekr."</string>
<string name="compat_mode_help_header" msgid="7969493989397529910">"Saderības tālummaiņa"</string>
<string name="compat_mode_help_body" msgid="4946726776359270040">"Ja lietotne ir paredzēta mazākam ekrānam, blakus pulkstenim tiks parādīta tālummaiņas vadīkla."</string>
- <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Notiek ekrānuzņ. saglabāšana"</string>
+ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saglabā ekrānuzņēmumu…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Notiek ekrānuzņēmuma saglabāšana..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Notiek ekrānuzņēmuma saglabāšana."</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrānuzņēmums ir uzņemts."</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index e607632..7a7c5a5 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -128,7 +128,7 @@
<string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3,5G"</string>
<string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
<string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
- <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Datastreifing"</string>
+ <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
<string name="accessibility_no_sim" msgid="8274017118472455155">"Uten SIM."</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index c454bb1..fff1aa0 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -157,14 +157,14 @@
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm ustawiony na <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Wyłączono transmisję danych 2G/3G"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Wyłączono transmisję danych 4G"</string>
- <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Wyłączono komórkową transmisję danych"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Transmisja danych została wyłączona"</string>
<string name="data_usage_disabled_dialog_title" msgid="2086815304858964954">"Wyłączono transmisję danych"</string>
- <string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Osiągnięto określony limit wykorzystania transmisji danych."\n\n"Jeśli ponownie włączysz przesyłanie danych, operator może naliczyć opłaty."</string>
+ <string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Ustawiony limit transmisji danych został osiągnięty."\n\n"Jeśli ponownie włączysz przesyłanie danych, operator może naliczyć dodatkowe opłaty."</string>
<string name="data_usage_disabled_dialog_enable" msgid="7729772039208664606">"Włącz transmisję danych"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Brak internetu"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: połączono"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Wyszukiwanie sygnału GPS"</string>
- <string name="gps_notification_found_text" msgid="4619274244146446464">"Lokalizacja ustawiona według GPS"</string>
+ <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>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a14ee38..0cfd805 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -41,7 +41,7 @@
<string name="battery_low_why" msgid="7279169609518386372">"Uso da bateria"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Configurações"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
- <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo para avião"</string>
+ <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo avião"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Girar automaticamente a tela"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"MUDO"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTO"</string>
@@ -133,7 +133,7 @@
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
<string name="accessibility_no_sim" msgid="8274017118472455155">"Sem SIM."</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Tethering Bluetooth."</string>
- <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo de avião."</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<!-- String.format failed for translation -->
<!-- no translation found for accessibility_battery_level (7451474187113371965) -->
<skip />
@@ -154,7 +154,7 @@
<string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Celular <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"Bateria <xliff:g id="STATE">%s</xliff:g>."</string>
- <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo para avião <xliff:g id="STATE">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo avião <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dados 2G e 3G desativados"</string>
@@ -178,7 +178,7 @@
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
<string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
- <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo para avião"</string>
+ <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo avião"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Carregado"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 4618559..44b4b4e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -20,7 +20,7 @@
<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="7164937344850004466">"UI sistem"</string>
- <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ştergeţi"</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>
@@ -47,7 +47,7 @@
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTOM."</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificări"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Conectat prin tethering prin Bluetooth"</string>
- <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configuraţi metode de intrare"</string>
+ <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Setaţi metode introducere text"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastatură fizică"</string>
<string name="usb_device_permission_prompt" msgid="834698001271562057">"Permiteţi aplicaţiei <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze dispozitivul USB?"</string>
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permiteţi aplicaţiei <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze accesoriul USB?"</string>
@@ -137,7 +137,7 @@
<string name="accessibility_battery_level" msgid="7451474187113371965">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> procente."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Setări de sistem."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificări."</string>
- <string name="accessibility_remove_notification" msgid="3603099514902182350">"Ştergeţi notificarea."</string>
+ <string name="accessibility_remove_notification" msgid="3603099514902182350">"Ștergeţi notificarea."</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS activat."</string>
<string name="accessibility_gps_acquiring" msgid="8959333351058967158">"Se obţine GPS."</string>
<string name="accessibility_tty_enabled" msgid="4613200365379426561">"TeleTypewriter activat."</string>
@@ -165,7 +165,7 @@
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectat"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Se caută GPS"</string>
<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="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>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ccf4fd48..0f398bc 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -77,7 +77,7 @@
<string name="use_ptp_button_title" msgid="7517127540301625751">"Установить как камеру (PTP)"</string>
<string name="installer_cd_button_title" msgid="2312667578562201583">"Установить приложение"</string>
<string name="accessibility_back" msgid="567011538994429120">"Назад"</string>
- <string name="accessibility_home" msgid="8217216074895377641">"Главная страница"</string>
+ <string name="accessibility_home" msgid="8217216074895377641">"Домой"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Меню"</string>
<string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string>
@@ -118,8 +118,8 @@
<string name="accessibility_two_bars" msgid="6437363648385206679">"два деления"</string>
<string name="accessibility_three_bars" msgid="2648241415119396648">"три деления"</string>
<string name="accessibility_signal_full" msgid="9122922886519676839">"надежный сигнал"</string>
- <string name="accessibility_desc_on" msgid="2385254693624345265">"ВКЛ"</string>
- <string name="accessibility_desc_off" msgid="6475508157786853157">"ВЫКЛ"</string>
+ <string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
+ <string name="accessibility_desc_off" msgid="6475508157786853157">"Выкл."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Подключено"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
@@ -202,7 +202,7 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нет сети"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
<string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Проектор Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Беспроводной проектор"</string>
+ <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wi-Fi-монитор"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркость"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОНАСТРОЙКА"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Это панель уведомлений"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 91214d1..a58c9a0 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -181,7 +181,7 @@
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Laddat"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> enheter)"</string>
- <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth inaktivt"</string>
+ <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth av"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ljusstyrka"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotera automatiskt"</string>
<string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotationen har låsts"</string>
@@ -196,9 +196,9 @@
<string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
- <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi är inaktiverat"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi visas"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös visning"</string>
+ <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi av"</string>
+ <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Trådlös skärm"</string>
+ <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös skärm"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
<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>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 139854c..d5c8282 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -40,7 +40,7 @@
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Mipangilio"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Mtandao-Hewa"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Hali ya Ndege"</string>
- <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Zungusha otomatiki skrini"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Skrini ijizungushe kiotomatiki"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"PUUZA"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"KIOTOMATIKI"</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Arifa"</string>
@@ -130,7 +130,7 @@
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Ukingo"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
<string name="accessibility_no_sim" msgid="8274017118472455155">"Hakuna SIM."</string>
- <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Ufungaji wa Bluetooth."</string>
+ <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Shiriki intaneti kwa Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modi ya ndege."</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Asilimia <xliff:g id="NUMBER">%d</xliff:g> ya betri"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Mipangilio ya mfumo."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7750e77..f43eabe 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -35,10 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
<string name="battery_low_title" msgid="2783104807551211639">"Підключіть зарядний пристрій"</string>
- <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея виснажується."</string>
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Акумулятор розряджається."</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"Залишилося <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
<string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується."\n"Використовуйте лише наданий у комплекті зарядний пристрій."</string>
- <string name="battery_low_why" msgid="7279169609518386372">"Викор. батареї"</string>
+ <string name="battery_low_why" msgid="7279169609518386372">"Використання акумулятора"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Налаштування"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим польоту"</string>
@@ -85,11 +85,11 @@
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
<string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetooth від’єднано."</string>
- <string name="accessibility_no_battery" msgid="358343022352820946">"Немає заряду батареї."</string>
- <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Одна смужка заряду батареї."</string>
- <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Дві смужки заряду батареї."</string>
- <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Три смужки заряду батареї."</string>
- <string name="accessibility_battery_full" msgid="8909122401720158582">"Повний заряд батареї"</string>
+ <string name="accessibility_no_battery" msgid="358343022352820946">"Акумулятор розряджений."</string>
+ <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Заряд акумулятора: одна смужка."</string>
+ <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Заряд акумулятора: дві смужки."</string>
+ <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Заряд акумулятора: три смужки."</string>
+ <string name="accessibility_battery_full" msgid="8909122401720158582">"Акумулятор заряджений."</string>
<string name="accessibility_no_phone" msgid="4894708937052611281">"Немає сигналу телефону."</string>
<string name="accessibility_phone_one_bar" msgid="687699278132664115">"Одна смужка сигналу телефону."</string>
<string name="accessibility_phone_two_bars" msgid="8384905382804815201">"Дві смужки сигналу телефону."</string>
@@ -134,7 +134,7 @@
<string name="accessibility_no_sim" msgid="8274017118472455155">"Немає SIM-карти."</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Прив’язка Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим польоту."</string>
- <string name="accessibility_battery_level" msgid="7451474187113371965">"Відсотків батареї: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора: <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Налаштування системи."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Сповіщення."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Очистити сповіщення."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 33d1f6d6d..3d2f2e1 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -68,9 +68,9 @@
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"正在保存屏幕截图..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"正在保存屏幕截图..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"正在保存屏幕截图。"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"已捕获屏幕截图。"</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"已抓取屏幕截图。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"触摸可查看您的屏幕截图。"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"无法捕获屏幕截图。"</string>
+ <string name="screenshot_failed_title" msgid="705781116746922771">"无法抓取屏幕截图。"</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"无法保存屏幕截图。存储设备可能正在使用中。"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB 文件传输选项"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"作为媒体播放器 (MTP) 装载"</string>
@@ -199,7 +199,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未连接"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"无网络"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已关闭"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi 显示设备"</string>
+ <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi 显示"</string>
<string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"无线显示"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 3b772be..5527735 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -53,7 +53,7 @@
<string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vumela insiza <xliff:g id="APPLICATION">%1$s</xliff:g> ukuthi ufinyelele ezintweni eziphuma ne-USB?"</string>
<string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vula <xliff:g id="ACTIVITY">%1$s</xliff:g> uma ledivayisi ye-USB ixhunyiwe?"</string>
<string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Vula <xliff:g id="ACTIVITY">%1$s</xliff:g> uma le-accessory ye-USB ixhunyiwe"</string>
- <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Azikho izinsiza ezisebenze ngezinto ze-USB. Funda okwengeziwe ngalento kwi <xliff:g id="URL">%1$s</xliff:g>"</string>
+ <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Azikho izinsiza ezisebenze ngezinto ze-USB. Funda okwengeziwe ngale into kwi <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ama-accessory e-USB"</string>
<string name="label_view" msgid="6304565553218192990">"Buka"</string>
<string name="always_use_device" msgid="1450287437017315906">"Sebenzisa ngokuzenzakalelayo yale divayisi ye-USB"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1f3e942..e1aed82 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -20,19 +20,13 @@
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;
public class SystemUIService extends Service {
static final String TAG = "SystemUIService";
@@ -69,10 +63,6 @@
@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 {
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/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 5041617..aa80664 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -197,15 +197,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/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 683824b..af72936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1172,4 +1172,12 @@
KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
return km.inKeyguardRestrictedInputMode();
}
+
+ public void suspendAutohide() {
+ // hook for subclasses
+ }
+
+ public void resumeAutohide() {
+ // hook for subclasses
+ }
}
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/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index edb3172..e8d4e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -132,6 +132,11 @@
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")
@@ -304,6 +309,15 @@
}
};
+ 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))
@@ -794,7 +808,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;
@@ -1384,6 +1398,8 @@
}
visibilityChanged(true);
+
+ suspendAutohide();
}
public void animateCollapsePanels() {
@@ -1666,6 +1682,9 @@
mPostCollapseCleanup.run();
mPostCollapseCleanup = null;
}
+
+ // Reschedule suspended auto-hide if necessary
+ resumeAutohide();
}
/**
@@ -1812,6 +1831,7 @@
hideCling();
}
+ suspendAutohide();
return false;
}
@@ -1855,10 +1875,57 @@
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();
+ }
+ }
+ notifyUiVisibilityChanged(mSystemUiVisibility);
}
}
+ @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) Slog.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);
@@ -1913,9 +1980,9 @@
}
}
- private void notifyUiVisibilityChanged() {
+ private void notifyUiVisibilityChanged(int vis) {
try {
- mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
+ mWindowManagerService.statusBarVisibilityChanged(vis);
} catch (RemoteException ex) {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java
index 8f2f5f9..0e8095c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FixedSizeDrawable.java
@@ -60,6 +60,10 @@
mDrawable.setAlpha(alpha);
}
+ public int getAlpha() {
+ return mDrawable.getAlpha();
+ }
+
public void setColorFilter(ColorFilter cf) {
mDrawable.setColorFilter(cf);
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/KeyguardServiceWrapper.java
new file mode 100644
index 0000000..e649125
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/KeyguardServiceWrapper.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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 onWakeKeyWhenKeyguardShowing(int keyCode) {
+ try {
+ mService.onWakeKeyWhenKeyguardShowing(keyCode);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ public void onWakeMotionWhenKeyguardShowing() {
+ try {
+ mService.onWakeMotionWhenKeyguardShowing();
+ } 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/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0fb3244..08fb48f 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;
@@ -160,6 +160,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,
@@ -204,13 +209,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;
@@ -231,7 +236,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;
@@ -279,7 +284,7 @@
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.
@@ -376,7 +381,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;
@@ -447,6 +452,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;
@@ -531,20 +539,29 @@
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 InputChannel mSystemGestureInputChannel;
+ private SystemGestures mSystemGestures;
+
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
if (mStatusBarService == null) {
@@ -608,7 +625,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;
@@ -618,7 +635,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;
}
}
@@ -626,7 +643,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;
}
}
@@ -670,7 +687,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());
@@ -733,7 +750,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);
}
}
@@ -827,10 +844,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 {
@@ -901,6 +914,35 @@
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(mMultiuserReceiver, filter);
+ // monitor for system gestures
+ mSystemGestureInputChannel = mWindowManagerFuncs.monitorInput("SystemGestures");
+ mSystemGestures = new SystemGestures(mSystemGestureInputChannel,
+ mHandler.getLooper(), context,
+ new SystemGestures.Callbacks() {
+ @Override
+ public void onSwipeFromTop() {
+ showHideybars(mStatusBar);
+ }
+ @Override
+ public void onSwipeFromBottom() {
+ if (mNavigationBarOnBottom) {
+ showHideybars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromRight() {
+ if (!mNavigationBarOnBottom) {
+ showHideybars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onDebug() {
+ if (OverlayTesting.ENABLED) {
+ OverlayTesting.toggleForceOverlay(mFocusedWindow, mContext);
+ }
+ }
+ });
+
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
@@ -1248,6 +1290,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:
@@ -1272,6 +1315,7 @@
!= PackageManager.PERMISSION_GRANTED;
}
+ @Override
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
@@ -1300,11 +1344,8 @@
}
}
- private boolean isBuiltInKeyboardVisible() {
- return mHaveBuiltInKeyboard && !isHidden(mLidKeyboardAccessibility);
- }
-
/** {@inheritDoc} */
+ @Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
@@ -1329,6 +1370,7 @@
}
/** {@inheritDoc} */
+ @Override
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return 2;
@@ -1364,54 +1406,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;
@@ -1502,6 +1547,7 @@
case TYPE_DREAM:
case TYPE_UNIVERSE_BACKGROUND:
case TYPE_KEYGUARD:
+ case TYPE_KEYGUARD_SCRIM:
return false;
default:
return true;
@@ -1675,7 +1721,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(
@@ -1698,6 +1744,13 @@
}
mKeyguard = win;
break;
+ case TYPE_KEYGUARD_SCRIM:
+ if (mKeyguardScrim != null) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ mKeyguardScrim = win;
+ break;
+
}
return WindowManagerGlobal.ADD_OKAY;
}
@@ -1707,8 +1760,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;
}
}
@@ -1952,6 +2010,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;
@@ -2070,6 +2129,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
@@ -2087,7 +2183,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: "
@@ -2113,7 +2209,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: "
@@ -2131,7 +2227,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: "
@@ -2278,7 +2374,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);
}
@@ -2293,7 +2389,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);
}
@@ -2312,12 +2408,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 {
@@ -2425,6 +2521,16 @@
@Override
public int adjustSystemUiVisibilityLw(int visibility) {
+ if (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;
@@ -2526,8 +2632,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
@@ -2553,12 +2659,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;
@@ -2575,7 +2682,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
@@ -2587,7 +2696,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;
@@ -2608,7 +2719,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;
@@ -2634,9 +2747,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
@@ -2660,9 +2778,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.
@@ -2673,24 +2793,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;
@@ -2701,6 +2831,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) {
@@ -2780,9 +2915,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);
}
@@ -2826,8 +2959,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
@@ -2857,11 +2989,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) {
@@ -2943,8 +3073,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
@@ -2958,11 +3088,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.
@@ -2972,11 +3100,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)) {
@@ -3052,14 +3178,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) {
@@ -3107,7 +3233,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)
@@ -3137,7 +3263,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);
}
@@ -3204,21 +3330,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;
@@ -3246,17 +3371,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
@@ -3265,8 +3390,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;
@@ -3283,11 +3412,11 @@
mStatusBarService = null;
}
}});
- } else if (DEBUG_LAYOUT) {
- Log.v(TAG, "Preventing status bar from hiding by policy");
+ } else {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Policy preventing status bar from 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;
}
}
@@ -3298,19 +3427,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);
}
});
}
@@ -3320,7 +3449,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) {
@@ -3330,11 +3459,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();
}
});
}
@@ -3345,7 +3474,7 @@
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
- mKeyguardMediator.setHidden(false);
+ mKeyguardDelegate.setHidden(false);
}
}
@@ -3396,7 +3525,7 @@
if (lidOpen) {
if (keyguardIsShowingTq()) {
- mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(KeyEvent.KEYCODE_POWER);
+ mKeyguardDelegate.onWakeKeyWhenKeyguardShowingTq(KeyEvent.KEYCODE_POWER);
} else {
mPowerManager.wakeUp(SystemClock.uptimeMillis());
}
@@ -3481,7 +3610,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 {
@@ -3576,10 +3706,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;
@@ -3618,7 +3748,7 @@
if (down && isWakeKey && isWakeKeyWhenScreenOff(keyCode)) {
if (keyguardActive) {
// If the keyguard is showing, let it wake the device when ready.
- mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
+ mKeyguardDelegate.onWakeKeyWhenKeyguardShowingTq(keyCode);
} else {
// Otherwise, wake the device ourselves.
result |= ACTION_WAKE_UP;
@@ -3888,9 +4018,9 @@
final boolean isWakeMotion = (policyFlags
& (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
if (isWakeMotion) {
- if (mKeyguardMediator != null && mKeyguardMediator.isShowing()) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
// If the keyguard is showing, let it decide what to do with the wake motion.
- mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq();
+ mKeyguardDelegate.onWakeMotionWhenKeyguardShowing();
} else {
// Otherwise, wake the device ourselves.
result |= ACTION_WAKE_UP;
@@ -3980,12 +4110,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();
}
}
}
@@ -4012,6 +4142,38 @@
}
};
+ private void showHideybars(WindowState swipeTarget) {
+ synchronized(mLock) {
+ 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);
@@ -4019,8 +4181,8 @@
mScreenOnEarly = false;
mScreenOnFully = false;
}
- if (mKeyguardMediator != null) {
- mKeyguardMediator.onScreenTurnedOff(why);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.onScreenTurnedOff(why);
}
synchronized (mLock) {
updateOrientationListenerLp();
@@ -4047,9 +4209,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);
@@ -4057,10 +4219,10 @@
});
return;
} else {
- mKeyguardMediator.onScreenTurnedOn(null);
+ mKeyguardDelegate.onScreenTurnedOn(null);
}
} else {
- Slog.i(TAG, "No keyguard mediator!");
+ Slog.i(TAG, "No keyguard interface!");
}
finishScreenTurningOn(screenOnListener);
}
@@ -4115,21 +4277,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();
}
@@ -4140,26 +4302,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();
}
}
});
@@ -4401,7 +4563,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) {
@@ -4413,17 +4575,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();
}
@@ -4530,8 +4694,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;
@@ -4559,7 +4723,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");
@@ -4574,6 +4738,7 @@
}
/** {@inheritDoc} */
+ @Override
public void enableScreenAfterBoot() {
readLidState();
applyLidSwitchState();
@@ -4615,7 +4780,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;
@@ -4670,7 +4835,7 @@
mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
}
-
+
/**
* goes to the home screen
* @return whether it did anything
@@ -4722,7 +4887,8 @@
}
return true;
}
-
+
+ @Override
public void setCurrentOrientationLw(int newOrientation) {
synchronized (mLock) {
if (newOrientation != mCurrentAppOrientation) {
@@ -4746,18 +4912,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;
@@ -4805,9 +4973,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);
}
}
@@ -4826,14 +4994,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()) {
@@ -4843,6 +5012,7 @@
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = mFocusedWindow.getAppToken();
mHandler.post(new Runnable() {
+ @Override
public void run() {
try {
IStatusBarService statusbar = getStatusBarService();
@@ -4859,8 +5029,118 @@
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 (!statusHideyAllowed) {
+ mStatusHideybar = HIDEYBAR_NONE;
+ if (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) {
+ 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) {
+ mNavigationBar.showLw(true);
+ }
+ }
+ }
+ return tmpVisibility;
+ }
+
+ // TODO temporary helper that allows testing overlay bars on existing apps
+ private static final class OverlayTesting {
+ static final boolean ENABLED = true;
+ 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;
}
@@ -4873,8 +5153,8 @@
@Override
public void setCurrentUserLw(int newUserId) {
- if (mKeyguardMediator != null) {
- mKeyguardMediator.setCurrentUser(newUserId);
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.setCurrentUser(newUserId);
}
if (mStatusBarService != null) {
try {
@@ -4888,7 +5168,7 @@
@Override
public void showAssistant() {
- mKeyguardMediator.showAssistant();
+ mKeyguardDelegate.showAssistant();
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/SystemGestures.java b/policy/src/com/android/internal/policy/impl/SystemGestures.java
new file mode 100644
index 0000000..c9731a5
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/SystemGestures.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.os.Looper;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+
+/*
+ * Listens for system-wide input gestures, firing callbacks when detected.
+ * @hide
+ */
+public class SystemGestures extends InputEventReceiver {
+ 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 SystemGestures(InputChannel inputChannel, Looper looper,
+ Context context, Callbacks callbacks) {
+ super(inputChannel, looper);
+ 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 onInputEvent(InputEvent event) {
+ if (event instanceof MotionEvent && event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ onPointerMotionEvent((MotionEvent) event);
+ }
+ finishInputEvent(event, false /*handled*/);
+ }
+
+ private void onPointerMotionEvent(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 79b66f4..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
+++ /dev/null
@@ -1,160 +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();
- }
-
- @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) {
- }
-}
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 fbeca4f..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ /dev/null
@@ -1,1672 +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 mPlaybackState;
- 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));
- mPlaybackState = dcs.playbackState;
-
- if (DEBUG) Log.v(TAG, "Initial transport state: "
- + mTransportState + ", pbstate=" + mPlaybackState);
- }
-
- 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;
- mTransportState = (clearing ? TRANSPORT_GONE : TRANSPORT_INVISIBLE);
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- @Override
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
- mPlaybackState = playbackState;
- if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
- if (mTransportState != TRANSPORT_GONE) {
- mTransportState = (isMusicPlaying(mPlaybackState) ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
- }
- 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 int children = mAppWidgetContainer.getChildCount();
- for (int i = 0; i < children; i++) {
- if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) {
- return i;
- }
- }
- 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 9d1f041..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ /dev/null
@@ -1,235 +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);
-
- 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);
- 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..2bb94be
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -0,0 +1,318 @@
+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.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;
+import com.android.internal.policy.impl.KeyguardServiceWrapper;
+
+/**
+ * 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 onWakeKeyWhenKeyguardShowingTq(int keycodePower) {
+ if (mKeyguardService != null) {
+ mKeyguardService.onWakeKeyWhenKeyguardShowing(keycodePower);
+ }
+ }
+
+ public void onWakeMotionWhenKeyguardShowing() {
+ if (mKeyguardService != null) {
+ mKeyguardService.onWakeMotionWhenKeyguardShowing();
+ }
+ }
+
+ 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/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 986dc49..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ /dev/null
@@ -1,956 +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;
-
-
- 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;
- }
- }
- };
-
- 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));
- }
- }
- };
-
- /**
- * 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 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);
-
- 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 368ccb3..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ /dev/null
@@ -1,129 +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 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 08a95a6..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ /dev/null
@@ -1,1434 +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;
-
- /**
- * 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);
- }
-
- @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;
- }
-}
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/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 186a013..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ /dev/null
@@ -1,2566 +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);
- 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/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..b18be1c
--- /dev/null
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 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..32fc18e 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -88,7 +88,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[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
private static final String DUMPSYS_DATA_PATH = "/data/system/";
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index f7a7fdf..ae7c806 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;
@@ -116,7 +115,6 @@
// used inside handler thread
private boolean mEnable;
private int mState;
- private HandlerThread mThread;
private final BluetoothHandler mHandler;
private void registerForAirplaneMode(IntentFilter filter) {
@@ -189,9 +187,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/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..6e24d68 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);
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 142357d..016a664 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -63,6 +63,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 +94,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 +143,6 @@
private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
- private HandlerThread mHandlerThread;
// --- fields below are protected by mWakeLock ---
private int mPendingBroadcasts;
@@ -216,9 +215,7 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
// 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);
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 d7adbf7..7857d6c 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -491,7 +491,6 @@
}
};
- private final HandlerThread mHandlerThread;
private final Handler mHandler;
void waitForAsecScan() {
@@ -827,7 +826,7 @@
}
if (code == VoldResponseCode.VolumeDiskInserted) {
- new Thread() {
+ new Thread("MountService#VolumeDiskInserted") {
@Override
public void run() {
try {
@@ -1114,7 +1113,7 @@
/*
* USB mass storage disconnected while enabled
*/
- new Thread() {
+ new Thread("MountService#AvailabilityChange") {
@Override
public void run() {
try {
@@ -1313,9 +1312,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();
@@ -1337,7 +1336,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 c3f2afa..abcd8ee 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 d2acb40..0d266c2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -990,7 +990,8 @@
mConnector.execute("softap", "set", wlanIface);
} else {
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), wifiConfig.preSharedKey);
+ "broadcast", getSecurityType(wifiConfig),
+ wifiConfig.preSharedKey);
}
mConnector.execute("softap", "startap");
} catch (NativeDaemonConnectorException e) {
@@ -1039,7 +1040,8 @@
mConnector.execute("softap", "set", wlanIface);
} else {
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), wifiConfig.preSharedKey);
+ "broadcast", getSecurityType(wifiConfig),
+ wifiConfig.preSharedKey);
}
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9455017..7d7aeec 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;
@@ -74,7 +75,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 +87,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 +154,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 +174,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 +191,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 +239,6 @@
pm = PackageManagerService.main(context, installer,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
- boolean firstBoot = false;
try {
firstBoot = pm.isFirstBoot();
} catch (RemoteException e) {
@@ -267,6 +257,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);
@@ -304,14 +295,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 +325,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 +348,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 +393,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 +408,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);
+ }
}
/*
@@ -563,28 +574,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 +610,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 +623,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 +632,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 +688,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 +732,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,6 +772,16 @@
}
}
+ 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);
@@ -773,10 +812,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 +896,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 +914,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 +1023,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 +1118,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/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 3aec4ea..7eedc2a 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;
@@ -83,15 +82,13 @@
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;
@@ -116,40 +113,75 @@
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 final boolean mCheckReboot;
+ private boolean mCompleted;
+ private Monitor mCurrentMonitor;
+
+ HandlerChecker(Handler handler, String name, boolean checkReboot) {
+ mHandler = handler;
+ mName = name;
+ mCheckReboot = checkReboot;
+ }
+
+ public void addMonitor(Monitor monitor) {
+ mMonitors.add(monitor);
+ }
+
+ public void scheduleCheckLocked() {
+ if (!mCheckReboot && 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);
+ }
+
+ public boolean isCompletedLocked() {
+ return mCompleted;
+ }
+
+ public String describeBlockedStateLocked() {
+ return mCurrentMonitor == null ? mName : mCurrentMonitor.getClass().getName();
}
@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 run() {
+ // See if we should force a reboot.
+ if (mCheckReboot) {
+ 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);
+ }
+ }
- final int size = mMonitors.size();
- for (int i = 0 ; i < size ; i++) {
- synchronized (Watchdog.this) {
- mCurrentMonitor = mMonitors.get(i);
- }
- mCurrentMonitor.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;
+ synchronized (Watchdog.this) {
+ mCompleted = true;
+ mCurrentMonitor = null;
}
}
}
@@ -194,9 +226,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", true);
+ 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", false));
+ // Add checker for shared UI thread.
+ mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", false));
+ // And also check IO thread.
+ mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", false));
}
public void init(Context context, BatteryService battery,
@@ -243,9 +289,18 @@
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);
+ }
+ }
+
+ public void addThread(Handler thread, String name) {
+ synchronized (this) {
+ if (isAlive()) {
+ throw new RuntimeException("Threads can't be added once the Watchdog is running");
+ }
+ mHandlerCheckers.add(new HandlerChecker(thread, name, false));
}
}
@@ -399,18 +454,46 @@
return newTime;
}
+ private boolean haveAllCheckersCompletedLocked() {
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String describeBlockedCheckersLocked() {
+ StringBuilder builder = new StringBuilder(128);
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(hc.describeBlockedStateLocked());
+ }
+ }
+ return builder.toString();
+ }
+
@Override
public void run() {
boolean waitedHalf = false;
while (true) {
- mCompleted = false;
- mHandler.sendEmptyMessage(MONITOR);
-
-
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 +509,7 @@
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
}
- if (mCompleted) {
+ if (haveAllCheckersCompletedLocked()) {
// The monitors have returned.
waitedHalf = false;
continue;
@@ -443,8 +526,7 @@
continue;
}
- name = (mCurrentMonitor != null) ?
- mCurrentMonitor.getClass().getName() : "null";
+ name = describeBlockedCheckersLocked();
allowRestart = mAllowRestart;
}
@@ -521,6 +603,12 @@
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
+ Slog.w(TAG, "Main thread stack trace:");
+ StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
+ for (StackTraceElement element: stackTrace) {
+ Slog.w(TAG, "\tat " + element);
+ }
+ Slog.w(TAG, "<End of main thread stack trace>");
Process.killProcess(Process.myPid());
System.exit(10);
}
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 3b63937..4f5008d 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);
}
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 5c24e67..10db70f 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -439,7 +439,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;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ba174b7..fad1bc5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,12 +17,20 @@
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 com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -33,12 +41,22 @@
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.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -125,6 +143,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 +151,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,7 +186,7 @@
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";
@@ -198,9 +218,10 @@
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_STACK = 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 +231,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;
@@ -270,10 +291,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 +311,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 +362,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;
@@ -382,7 +412,7 @@
* The currently running heavy-weight process, if any.
*/
ProcessRecord mHeavyWeightProcess = null;
-
+
/**
* The last time that various processes have crashed.
*/
@@ -418,7 +448,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
@@ -460,7 +490,7 @@
* 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 +535,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 +563,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.
@@ -600,13 +626,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 +640,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 +685,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 +708,7 @@
* configurations.
*/
int mConfigurationSeq = 0;
-
+
/**
* Hardware-reported OpenGLES version.
*/
@@ -686,7 +724,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 +745,7 @@
int mFactoryTest;
boolean mCheckedForSetup;
-
+
/**
* The time at which we will allow normal application switches again,
* after a call to {@link #stopAppSwitches()}.
@@ -719,7 +757,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 +790,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;
@@ -793,13 +823,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 +869,7 @@
* protect all related state.
*/
final Thread mProcessStatsThread;
-
+
/**
* Used to collect process stats when showing not responding dialog.
* Protected by mProcessStatsThread.
@@ -870,8 +900,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 +918,7 @@
mAppThread = thread;
}
+ @Override
public void binderDied() {
if (localLOGV) Slog.v(
TAG, "Death received in " + this
@@ -927,10 +957,13 @@
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 SET_FOCUSED_STACK = 39;
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 +980,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 +1019,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 +1051,7 @@
killAppAtUsersRequest(proc, null);
}
}
-
+
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_MSG: {
@@ -1202,13 +1236,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 +1260,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,
@@ -1409,6 +1443,22 @@
}
break;
}
+ case PERSIST_URI_GRANTS: {
+ writeGrantedUriPermissions();
+ break;
+ }
+ case SET_FOCUSED_STACK: {
+ synchronized (ActivityManagerService.this) {
+ ActivityStack stack = mStackSupervisor.getStack(msg.arg1);
+ if (stack != null) {
+ ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r != null) {
+ setFocusedActivityLocked(r);
+ }
+ }
+ }
+ break;
+ }
}
}
};
@@ -1430,7 +1480,7 @@
mSelf.mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
mSystemThread.installSystemApplicationInfo(info);
-
+
synchronized (mSelf) {
ProcessRecord app = mSelf.newProcessRecordLocked(
mSystemThread.getApplicationThread(), info,
@@ -1452,6 +1502,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 +1532,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 +1546,14 @@
}
m.startRunning(null, null, null, null);
-
+
return context;
}
public static ActivityManagerService self() {
return mSelf;
}
-
+
static class AThread extends Thread {
ActivityManagerService mService;
Looper mLooper;
@@ -1510,6 +1563,7 @@
super("ActivityManager");
}
+ @Override
public void run() {
Looper.prepare();
@@ -1630,7 +1684,7 @@
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;
@@ -1653,6 +1707,9 @@
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 +1725,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 {
@@ -1793,7 +1851,7 @@
(softIrq * 100) / total);
}
}
-
+
long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes();
final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
@@ -1843,7 +1901,7 @@
}
}
}
-
+
@Override
public void batteryNeedsCpuUpdate() {
updateCpuStatsNow();
@@ -1882,6 +1940,7 @@
final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
mFocusedActivity = r;
+ mStackSupervisor.setFocusedStack(r);
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
@@ -1889,6 +1948,11 @@
}
}
+ @Override
+ public void setFocusedStack(int stackId) {
+ mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget();
+ }
+
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 +1963,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) {
@@ -1946,7 +2017,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 +2075,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,
@@ -2041,12 +2112,12 @@
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName);
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
@@ -2116,7 +2187,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 +2203,10 @@
mProcessesOnHold.remove(app);
updateCpuStats();
-
+
System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1);
mProcDeaths[0] = 0;
-
+
try {
int uid = app.uid;
@@ -2218,16 +2289,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 +2340,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);
+ }
}
}
@@ -2298,7 +2377,6 @@
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
}
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
@@ -2313,8 +2391,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);
}
}
@@ -2332,7 +2409,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
-
+
if (info != null) {
ai = info.activityInfo;
}
@@ -2352,7 +2429,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.
@@ -2361,12 +2438,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++) {
@@ -2376,7 +2453,7 @@
break;
}
}
-
+
if (ri != null) {
String vers = ri.activityInfo.metaData != null
? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
@@ -2391,13 +2468,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);
}
@@ -2408,6 +2485,7 @@
}
}
+ @Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (this) {
@@ -2415,6 +2493,7 @@
}
}
+ @Override
public void setFrontActivityScreenCompatMode(int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
@@ -2423,6 +2502,7 @@
}
}
+ @Override
public int getPackageScreenCompatMode(String packageName) {
enforceNotIsolatedCaller("getPackageScreenCompatMode");
synchronized (this) {
@@ -2430,6 +2510,7 @@
}
}
+ @Override
public void setPackageScreenCompatMode(String packageName, int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageScreenCompatMode");
@@ -2438,6 +2519,7 @@
}
}
+ @Override
public boolean getPackageAskScreenCompat(String packageName) {
enforceNotIsolatedCaller("getPackageAskScreenCompat");
synchronized (this) {
@@ -2445,6 +2527,7 @@
}
}
+ @Override
public void setPackageAskScreenCompat(String packageName, boolean ask) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageAskScreenCompat");
@@ -2453,11 +2536,6 @@
}
}
- void reportResumedActivityLocked(ActivityRecord r) {
- //Slog.i(TAG, "**** REPORT RESUME: " + r);
- updateUsageStats(r, true);
- }
-
private void dispatchProcessesChanged() {
int N;
synchronized (this) {
@@ -2470,6 +2548,7 @@
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "*** Delivering " + N + " process changes");
}
+
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
@@ -2521,12 +2600,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,
@@ -2536,6 +2616,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,
@@ -2543,11 +2624,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,
@@ -2556,12 +2639,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,
@@ -2569,12 +2654,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,
@@ -2584,20 +2671,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;
}
}
@@ -2605,7 +2692,8 @@
resultTo, resultWho, requestCode, flagsMask, flagsValues, options);
return ret;
}
-
+
+ @Override
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) {
// Refuse possible leaked file descriptors
@@ -2614,7 +2702,7 @@
}
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(callingActivity);
+ final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
if (r == null) {
ActivityOptions.abort(options);
return false;
@@ -2688,7 +2776,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);
@@ -2709,19 +2797,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;
}
@@ -2732,7 +2823,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;
}
@@ -2765,31 +2857,31 @@
mRecentTasks.add(0, task);
}
- public void setRequestedOrientation(IBinder token,
- int requestedOrientation) {
+ @Override
+ public void setRequestedOrientation(IBinder token, int requestedOrientation) {
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ 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;
}
@@ -2799,13 +2891,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) {
@@ -2813,9 +2906,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;
@@ -2825,20 +2922,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) {
@@ -2849,31 +2947,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)
@@ -2885,10 +2981,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.
@@ -2909,14 +3005,14 @@
}
}
}
-
+
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!");
@@ -2931,61 +3027,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
@@ -3002,21 +3103,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);
@@ -3024,19 +3114,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) {
@@ -3065,7 +3142,7 @@
IApplicationThread thread) {
mProcDeaths[0]++;
-
+
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid);
@@ -3097,7 +3174,7 @@
break;
}
}
-
+
if (!haveBg) {
EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
long now = SystemClock.uptimeMillis();
@@ -3179,6 +3256,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(); }
};
@@ -3344,7 +3422,7 @@
if (MONITOR_CPU_USAGE) {
updateCpuStatsNow();
}
-
+
synchronized (this) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mShuttingDown) {
@@ -3357,7 +3435,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;
@@ -3368,11 +3446,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--) {
@@ -3447,7 +3525,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");
@@ -3456,16 +3534,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;
@@ -3473,7 +3551,7 @@
if (activity != null) {
map.put("activity", activity);
}
-
+
mHandler.sendMessage(msg);
}
}
@@ -3501,7 +3579,8 @@
});
}
}
-
+
+ @Override
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
@@ -3540,10 +3619,14 @@
"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);
@@ -3557,6 +3640,7 @@
return true;
}
+ @Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
@@ -3593,6 +3677,7 @@
}
}
+ @Override
public void killAllBackgroundProcesses() {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3603,7 +3688,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+
long callingId = Binder.clearCallingIdentity();
try {
synchronized(this) {
@@ -3624,7 +3709,7 @@
}
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background");
@@ -3635,6 +3720,7 @@
}
}
+ @Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3683,6 +3769,7 @@
/*
* The pkg name and app id have to be specified.
*/
+ @Override
public void killApplicationWithAppId(String pkg, int appid) {
if (pkg == null) {
return;
@@ -3707,6 +3794,7 @@
}
}
+ @Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
@@ -3744,21 +3832,15 @@
}
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--) {
@@ -3768,7 +3850,8 @@
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--) {
@@ -3777,6 +3860,7 @@
return pss;
}
+ @Override
public void killApplicationProcess(String processName, int uid) {
if (processName == null) {
return;
@@ -3894,7 +3978,7 @@
procs.add(app);
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
@@ -3960,37 +4044,12 @@
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--;
- }
+
+ if (mStackSupervisor.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
+ if (!doit) {
+ return true;
}
+ didSomething = true;
}
if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) {
@@ -4018,6 +4077,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
@@ -4078,11 +4140,11 @@
}
}
if (mBooted) {
- mMainStack.resumeTopActivityLocked(null);
- mMainStack.scheduleIdleLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.scheduleIdleLocked();
}
}
-
+
return didSomething;
}
@@ -4112,7 +4174,7 @@
handleAppDiedLocked(app, true, allowRestart);
mLruProcesses.remove(app);
Process.killProcessQuiet(pid);
-
+
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
addAppLocked(app.info, false);
@@ -4123,7 +4185,7 @@
} else {
mRemovedProcesses.add(app);
}
-
+
return needRestart;
}
@@ -4137,7 +4199,7 @@
gone = true;
}
}
-
+
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
@@ -4217,7 +4279,7 @@
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);
@@ -4243,7 +4305,7 @@
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);
@@ -4332,23 +4394,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;
}
}
@@ -4364,7 +4416,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;
@@ -4399,6 +4451,7 @@
return true;
}
+ @Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
@@ -4408,13 +4461,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) {
@@ -4437,11 +4493,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();
@@ -4451,7 +4509,7 @@
mLockScreenShown = false;
comeOutOfSleepIfNeededLocked();
}
- mMainStack.dismissKeyguardOnNextActivityLocked();
+ mStackSupervisor.setDismissKeyguard(true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -4537,18 +4595,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(
@@ -4564,9 +4635,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);
}
}
@@ -4579,11 +4650,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);
@@ -4591,6 +4669,7 @@
}
}
+ @Override
public ComponentName getCallingActivity(IBinder token) {
synchronized (this) {
ActivityRecord r = getCallingRecordLocked(token);
@@ -4599,16 +4678,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;
}
@@ -4616,9 +4696,10 @@
}
}
+ @Override
public String getPackageForToken(IBinder token) {
synchronized(this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return null;
}
@@ -4626,6 +4707,7 @@
}
}
+ @Override
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
@@ -4696,7 +4778,7 @@
}
}
}
-
+
IIntentSender getIntentSenderLocked(int type, String packageName,
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
@@ -4705,7 +4787,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;
}
@@ -4762,6 +4844,7 @@
return rec;
}
+ @Override
public void cancelIntentSender(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
return;
@@ -4795,6 +4878,7 @@
}
}
+ @Override
public String getPackageForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
@@ -4807,6 +4891,7 @@
return null;
}
+ @Override
public int getUidForIntentSender(IIntentSender sender) {
if (sender instanceof PendingIntentRecord) {
try {
@@ -4818,6 +4903,7 @@
return -1;
}
+ @Override
public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
@@ -4839,6 +4925,7 @@
return false;
}
+ @Override
public boolean isIntentSenderAnActivity(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
@@ -4854,6 +4941,7 @@
return false;
}
+ @Override
public Intent getIntentForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
@@ -4866,6 +4954,7 @@
return null;
}
+ @Override
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
@@ -4876,6 +4965,7 @@
trimApplications();
}
+ @Override
public int getProcessLimit() {
synchronized (this) {
return mProcessLimitOverride;
@@ -4901,7 +4991,8 @@
updateOomAdjLocked();
}
}
-
+
+ @Override
public void setProcessForeground(IBinder token, int pid, boolean isForeground) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessForeground()");
@@ -4925,6 +5016,7 @@
}
if (isForeground && token != null) {
ForegroundToken newToken = new ForegroundToken() {
+ @Override
public void binderDied() {
foregroundTokenDied(this);
}
@@ -4959,6 +5051,7 @@
mActivityManagerService = activityManagerService;
}
+ @Override
public boolean checkPermission(String permission, int pid, int uid) {
return mActivityManagerService.checkPermission(permission, pid,
uid) == PackageManager.PERMISSION_GRANTED;
@@ -4966,12 +5059,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;
}
@@ -5010,6 +5105,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;
@@ -5131,6 +5227,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.
@@ -5144,6 +5272,7 @@
return (modeFlags&perm.modeFlags) == modeFlags;
}
+ @Override
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
enforceNotIsolatedCaller("checkUriPermission");
@@ -5177,6 +5306,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) {
@@ -5197,20 +5327,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;
@@ -5232,7 +5350,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);
@@ -5251,7 +5369,7 @@
allowed = false;
}
}
- if (allowed) {
+ if (allowed && !persist) {
return -1;
}
}
@@ -5295,6 +5413,7 @@
return targetUid;
}
+ @Override
public int checkGrantUriPermission(int callingUid, String targetPkg,
Uri uri, int modeFlags) {
enforceNotIsolatedCaller("checkGrantUriPermission");
@@ -5303,8 +5422,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) {
@@ -5317,32 +5437,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();
}
}
@@ -5365,10 +5473,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;
}
}
@@ -5396,11 +5504,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);
}
@@ -5409,12 +5517,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);
}
@@ -5458,6 +5566,7 @@
grantUriPermissionUncheckedFromIntentLocked(needed, owner);
}
+ @Override
public void grantUriPermission(IApplicationThread caller, String targetPkg,
Uri uri, int modeFlags) {
enforceNotIsolatedCaller("grantUriPermission");
@@ -5484,44 +5593,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;
@@ -5537,6 +5627,8 @@
//}
}
+ boolean persistChanged = false;
+
// Go through all of the permissions and remove any that match.
final List<String> SEGMENTS = uri.getPathSegments();
if (SEGMENTS != null) {
@@ -5566,8 +5658,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();
}
@@ -5580,6 +5672,11 @@
}
}
}
+
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
+ }
}
public void revokeUriPermission(IApplicationThread caller, Uri uri,
@@ -5597,6 +5694,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) {
@@ -5604,26 +5702,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();
}
}
@@ -5678,6 +5814,103 @@
}
}
+ 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);
+ }
+ }
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
@@ -5712,12 +5945,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) {
@@ -5743,88 +5976,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);
@@ -5846,6 +6011,7 @@
return list;
}
+ @Override
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags, int userId) {
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -5918,49 +6084,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);
}
@@ -5968,6 +6140,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);
@@ -6014,43 +6188,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);
@@ -6059,46 +6220,10 @@
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()");
@@ -6111,34 +6236,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);
}
@@ -6146,21 +6244,25 @@
}
}
- 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) {
+ 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);
}
}
@@ -6173,19 +6275,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()");
@@ -6205,27 +6309,69 @@
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 relativeStackId, int position, float weight) {
+ synchronized (this) {
+ if (mStackSupervisor.getStack(relativeStackId) == null) {
+ return -1;
+ }
+ int stackId = mStackSupervisor.createStack();
+ mWindowManager.createStack(stackId, relativeStackId, position, weight);
+ if (taskId > 0) {
+ moveTaskToStack(taskId, stackId, true);
+ }
+ return stackId;
}
}
- 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) {
+ synchronized (this) {
+ mWindowManager.moveTaskToStack(taskId, stackId, toTop);
+ mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
}
+ }
- return -1;
+ @Override
+ public void resizeStack(int stackId, float weight) {
+ mWindowManager.resizeStack(stackId, weight);
+ }
+
+ @Override
+ public List<ActivityManager.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;
+ }
+ }
+
+ @Override
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+ synchronized(this) {
+ return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+ }
}
// =========================================================
@@ -6249,7 +6395,7 @@
synchronized(this) {
if (r == null) {
- r = mMainStack.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
@@ -6647,7 +6793,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);
}
@@ -7133,13 +7279,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 +7323,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 +7340,7 @@
if (!mSleeping) {
mSleeping = true;
- mMainStack.stopIfSleepingLocked();
+ mStackSupervisor.goingToSleepLocked();
// Initialize the wake times of all processes.
checkExcessivePowerUsageLocked(false);
@@ -7208,42 +7351,26 @@
}
}
+ @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();
-
+
return timedout;
}
@@ -7256,9 +7383,9 @@
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- r = mMainStack.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- mMainStack.activitySleptLocked(r);
+ r.task.stack.activitySleptLocked(r);
}
}
@@ -7269,8 +7396,7 @@
if (!mWentToSleep && !mLockScreenShown) {
if (mSleeping) {
mSleeping = false;
- mMainStack.awakeFromSleepingLocked();
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
}
}
@@ -7427,6 +7553,7 @@
}
}
+ @Override
public void setAlwaysFinish(boolean enabled) {
enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH,
"setAlwaysFinish()");
@@ -7440,6 +7567,7 @@
}
}
+ @Override
public void setActivityController(IActivityController controller) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"setActivityController()");
@@ -7449,6 +7577,7 @@
}
}
+ @Override
public void setUserIsMonkey(boolean userIsMonkey) {
synchronized (this) {
synchronized (mPidsSelfLocked) {
@@ -7466,6 +7595,7 @@
}
}
+ @Override
public boolean isUserAMonkey() {
synchronized (this) {
// If there is a controller also implies the user is a monkey.
@@ -7562,7 +7692,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;
@@ -7629,7 +7759,7 @@
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();
}
@@ -7647,7 +7777,7 @@
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 +7788,7 @@
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
- ActivityRecord r = mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
return (r != null) ? r.immersive : false;
}
}
@@ -8146,6 +8276,10 @@
retrieveSettings();
+ synchronized (this) {
+ readGrantedUriPermissionsLocked();
+ }
+
if (goingCallback != null) goingCallback.run();
synchronized (this) {
@@ -8207,7 +8341,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivitiesLocked();
sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
}
}
@@ -8302,15 +8436,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 +8456,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,9 +8481,9 @@
// 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();
+ ActivityRecord r = it.next();
if (r.isHomeActivity) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
@@ -9388,50 +9488,14 @@
boolean 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,
- 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);
- }
+
+ mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage);
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);
- }
+ pw.println(" ");
+
+ mStackSupervisor.dump(pw, " ");
if (mRecentTasks.size() > 0) {
pw.println();
@@ -9453,12 +9517,7 @@
}
}
}
-
- if (dumpAll) {
- pw.println(" ");
- pw.println(" mCurTask: " + mCurTask);
- }
-
+
return true;
}
@@ -9675,7 +9734,7 @@
}
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
@@ -9735,8 +9794,8 @@
pw.print(" mLastPowerCheckUptime=");
TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
pw.println("");
- pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
+ pw.println(" mGoingToSleep=" + getFocusedStack().mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + getFocusedStack().mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs
+ " mNumHiddenProcs=" + mNumHiddenProcs
@@ -9920,32 +9979,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) {
@@ -10197,75 +10234,6 @@
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;
- }
- }
- }
-
private static String buildOomTag(String prefix, String space, int val, int base) {
if (val == base) {
if (space == null) return prefix;
@@ -11987,6 +11955,9 @@
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);
}
}
}
@@ -12563,6 +12534,10 @@
return config;
}
+ ActivityStack getFocusedStack() {
+ return mStackSupervisor.getFocusedStack();
+ }
+
public Configuration getConfiguration() {
Configuration ci;
synchronized(this) {
@@ -12624,9 +12599,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 +12677,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 +12713,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 +12739,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;
}
}
@@ -13594,8 +13486,7 @@
}
}
return !processingBroadcasts
- && (mSleeping || (mMainStack.mResumedActivity != null &&
- mMainStack.mResumedActivity.idle));
+ && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
}
/**
@@ -13879,14 +13770,7 @@
}
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) {
@@ -14129,7 +14013,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 +14087,7 @@
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");
}
}
@@ -14503,7 +14387,6 @@
}
mCurrentUserId = userId;
- mCurrentUserArray = new int[] { userId };
final Integer userIdInt = Integer.valueOf(userId);
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
@@ -14569,7 +14452,7 @@
}
}
- boolean haveActivities = mMainStack.switchUserLocked(userId, uss);
+ boolean haveActivities = mStackSupervisor.switchUserLocked(userId, uss);
if (!haveActivities) {
startHomeActivityLocked(userId);
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 054d213..51b9984 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -20,8 +20,8 @@
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 +54,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.
@@ -95,9 +97,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 +130,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());
@@ -182,7 +188,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");
@@ -269,36 +275,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 +309,7 @@
return 0;
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Token{");
@@ -326,13 +330,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 +364,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
@@ -413,10 +417,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,30 +429,24 @@
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())) {
+
+ // 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.
+ isHomeActivity =
+ (!_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!
- // 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;
- }
- } else {
- isHomeActivity = false;
- }
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
} else {
@@ -468,6 +466,9 @@
}
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--;
@@ -504,6 +505,7 @@
inHistory = false;
if (task != null && !finishing) {
task.numActivities--;
+ task = null;
}
clearOptionsLocked();
}
@@ -525,6 +527,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 +545,7 @@
ActivityResult r = new ActivityResult(from, resultWho,
requestCode, resultCode, resultData);
if (results == null) {
- results = new ArrayList();
+ results = new ArrayList<ResultInfo>();
}
results.add(r);
}
@@ -563,11 +570,11 @@
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.
@@ -583,7 +590,7 @@
// case we will deliver it if this is the current top activity on its
// stack.
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>();
@@ -701,9 +708,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,6 +731,7 @@
boolean continueLaunchTickingLocked() {
if (launchTickTime != 0) {
+ final ActivityStack stack = task.stack;
Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG);
msg.obj = this;
stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
@@ -738,7 +743,7 @@
void finishLaunchTickingLocked() {
launchTickTime = 0;
- stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
+ task.stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
}
// IApplicationToken
@@ -750,23 +755,24 @@
// 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 windowsDrawn() {
synchronized(service) {
if (launchTime != 0) {
+ final ActivityStack stack = task.stack;
final long curTime = SystemClock.uptimeMillis();
final long thisTime = curTime - launchTime;
final long totalTime = stack.mInitialStartTime != 0
@@ -788,7 +794,7 @@
}
Log.i(ActivityManagerService.TAG, sb.toString());
}
- stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+ mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
@@ -802,7 +808,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 +818,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 +848,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 +866,7 @@
r = this;
}
}
-
+
return r;
}
@@ -875,7 +879,7 @@
}
return service.inputDispatchingTimedOut(anrApp, r, this, false);
}
-
+
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
@@ -889,7 +893,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,6 +904,7 @@
if (app != null && app.thread != null) {
try {
app.thread.scheduleSleeping(appToken, _sleeping);
+ final ActivityStack stack = task.stack;
if (sleeping && !stack.mGoingToSleepActivities.contains(this)) {
stack.mGoingToSleepActivities.add(this);
}
@@ -910,10 +915,48 @@
}
}
}
-
+
+ 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 +965,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/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 0f1700d..e39ea4a 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -16,33 +16,30 @@
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.ActivityStackSupervisor.HOME_STACK_ID;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
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.IActivityController;
+import android.app.IThumbnailReceiver;
import android.app.IApplicationThread;
-import android.app.PendingIntent;
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 +51,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;
@@ -85,17 +81,14 @@
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 DEBUG_STACK = ActivityManagerService.DEBUG_STACK;
+
+ static final boolean DEBUG_STATES = ActivityStackSupervisor.DEBUG_STATES;
+ static final boolean DEBUG_ADD_REMOVE = ActivityStackSupervisor.DEBUG_ADD_REMOVE;
+ static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
+ static final boolean DEBUG_APP = ActivityStackSupervisor.DEBUG_APP;
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;
@@ -119,19 +112,19 @@
// 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;
-
+
enum ActivityState {
INITIALIZING,
RESUMED,
@@ -145,20 +138,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,56 +161,15 @@
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>();
+ 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>();
+ final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<ActivityRecord>();
/**
* Set when the system is going to sleep, until we have
@@ -251,39 +203,28 @@
* 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;
-
+
/**
* 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;
-
- /**
* Save the most recent screenshot for reuse. This keeps Recents from taking two identical
* screenshots, one for the Recents thumbnail and one for the pauseActivity thumbnail.
*/
@@ -293,18 +234,20 @@
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
- private int mCurrentUser;
+ int mCurrentUser;
+
+ final int mStackId;
+
+ /** Run all ActivityStacks through this */
+ final ActivityStackSupervisor mStackSupervisor;
static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG;
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 LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
+ static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
+ static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+ static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+ static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
static class ScheduleDestroyArgs {
final ProcessRecord mOwner;
@@ -332,7 +275,7 @@
switch (msg.what) {
case SLEEP_TIMEOUT_MSG: {
synchronized (mService) {
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
mSleepTimeout = true;
checkReadyForSleepLocked();
@@ -349,23 +292,8 @@
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;
@@ -381,11 +309,9 @@
// 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);
+ synchronized (mService) {
+ activityDestroyedLocked(r != null ? r.appToken : null);
+ }
} break;
case LAUNCH_TIMEOUT_MSG: {
if (mService.mDidDexOpt) {
@@ -401,11 +327,6 @@
}
}
} break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (mService) {
- resumeTopActivityLocked(null);
- }
- } break;
case STOP_TIMEOUT_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
@@ -427,16 +348,28 @@
}
}
- 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 =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
mLaunchingActivity.setReferenceCounted(false);
+ mStackId = stackId;
+ mCurrentUser = service.mCurrentUserId;
}
private boolean okToShow(ActivityRecord r) {
@@ -445,25 +378,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 +408,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 +553,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,227 +576,50 @@
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);
+ 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);
-
+ void setLaunchTime(ActivityRecord r) {
if (r.launchTime == 0) {
r.launchTime = SystemClock.uptimeMillis();
if (mInitialStartTime == 0) {
@@ -816,27 +628,10 @@
} else if (mInitialStartTime == 0) {
mInitialStartTime = SystemClock.uptimeMillis();
}
-
- 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);
}
-
+
void stopIfSleepingLocked() {
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
if (!mGoingToSleep.isHeld()) {
mGoingToSleep.acquire();
if (mLaunchingActivity.isHeld()) {
@@ -858,9 +653,12 @@
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) {
+ // TODO: Skip if finishing?
+ activities.get(activityNdx).setSleeping(false);
+ }
}
mGoingToSleepActivities.clear();
}
@@ -871,7 +669,7 @@
}
void checkReadyForSleepLocked() {
- if (!mService.isSleeping()) {
+ if (!mService.isSleepingOrShuttingDown()) {
// Do not care.
return;
}
@@ -890,11 +688,11 @@
return;
}
- if (mStoppingActivities.size() > 0) {
+ if (mStackSupervisor.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();
+ + mStackSupervisor.mStoppingActivities.size() + " activities");
+ mStackSupervisor.scheduleIdleLocked();
return;
}
@@ -902,10 +700,13 @@
// 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);
- if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
- r.setSleeping(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.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
+ r.setSleeping(true);
+ }
}
}
@@ -931,7 +732,7 @@
if (who.noDisplay) {
return null;
}
-
+
Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
@@ -947,7 +748,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 +758,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);
@@ -980,18 +780,16 @@
prev.updateThumbnail(screenshotActivities(prev), null);
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,7 +803,7 @@
// 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) {
+ if (!mService.isSleepingOrShuttingDown()) {
mLaunchingActivity.acquire();
if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
// To be safe, don't allow the wake lock to be held for too long.
@@ -1014,7 +812,6 @@
}
}
-
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 +835,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 +886,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 +911,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 +920,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,13 +933,15 @@
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();
}
@@ -1173,45 +953,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);
+ 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 +1000,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,128 +1026,137 @@
}
/**
+ * 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 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 {
+ 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);
+ 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);
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Scheduling invisibility: " + r);
r.app.thread.scheduleWindowVisibility(r.appToken, false);
}
} catch (Exception e) {
@@ -1395,31 +1166,15 @@
+ 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;
}
/**
- * Version of ensureActivitiesVisible that can easily be called anywhere.
- */
- final void ensureActivitiesVisibleLocked(ActivityRecord starting,
- int configChanges) {
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- ensureActivitiesVisibleLocked(r, starting, null, configChanges);
- }
- }
-
- /**
* Ensure that the top activity in the stack is resumed.
*
* @param prev The previously resumed activity, for when in the process
@@ -1438,42 +1193,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 +1265,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);
+ mStackSupervisor.mStoppingActivities.remove(next);
mGoingToSleepActivities.remove(next);
next.sleeping = false;
- mWaitingVisibleActivities.remove(next);
+ mStackSupervisor.mWaitingVisibleActivities.remove(next);
next.updateOptionsLocked(options);
@@ -1499,9 +1282,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 +1316,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 +1335,7 @@
// happen whenever it needs to later.
mService.updateLruProcessLocked(next.app, false);
}
- startPausingLocked(userLeaving, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1569,7 +1357,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 +1370,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 +1398,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 +1492,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,13 +1523,13 @@
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();
} catch (Exception e) {
@@ -1755,22 +1537,22 @@
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.windowFlags,
+ null, true);
}
- startSpecificActivityLocked(next, true, false);
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1785,6 +1567,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,7 +1578,7 @@
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),
@@ -1805,42 +1588,44 @@
}
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 +1634,7 @@
return;
}
break;
- }
- if (p.fullscreen) {
+ } else if (task.numFullscreen > 0) {
startIt = false;
}
}
@@ -1858,28 +1642,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 +1676,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,11 +1710,15 @@
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,
@@ -1943,8 +1728,8 @@
} 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 +1738,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 +1807,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 +1972,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 +2082,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 +2109,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 +2128,7 @@
}
if (r.app != null && r.app.thread != null) {
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
if (mService.mFocusedActivity == r) {
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
}
@@ -3427,10 +2142,10 @@
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);
@@ -3451,211 +2166,22 @@
}
}
}
-
- 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) {
+ final ActivityRecord activityIdleInternalLocked(final IBinder token) {
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.
+ ActivityRecord res = isInStackLocked(token);
+ if (res != null) {
+ // No longer need to keep the device awake.
+ if (mResumedActivity == res && mLaunchingActivity.isHeld()) {
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mLaunchingActivity.release();
}
- // 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) {
- resumeTopActivityLocked(null);
+ // If this activity is fullscreen, set up to hide those under it.
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + res);
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
return res;
@@ -3667,63 +2193,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;
}
@@ -3759,17 +2304,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;
@@ -3779,53 +2315,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");
@@ -3836,8 +2368,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);
}
@@ -3845,33 +2376,24 @@
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();
}
@@ -3886,9 +2408,9 @@
}
// make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
+ mStackSupervisor.mStoppingActivities.remove(r);
mGoingToSleepActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
if (mResumedActivity == r) {
mResumedActivity = null;
}
@@ -3904,19 +2426,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
@@ -3946,9 +2548,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) {
@@ -3961,14 +2563,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.
@@ -3976,9 +2578,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();
}
@@ -3991,22 +2593,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.
*/
@@ -4031,36 +2637,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();
+
}
}
@@ -4087,10 +2697,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(
@@ -4103,7 +2710,7 @@
}
boolean skipDestroy = false;
-
+
try {
if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
@@ -4121,7 +2728,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
@@ -4158,46 +2765,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!");
@@ -4209,100 +2813,90 @@
boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
- removeHistoryRecordsForAppLocked(mStoppingActivities, app, "mStoppingActivities");
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+ "mStoppingActivities");
removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app, "mGoingToSleepActivities");
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app,
+ 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 (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);
+ }
+ 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 (ActivityStack.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) {
@@ -4313,16 +2907,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 || index == numTasks - 1) {
// nothing to do!
if (reason != null &&
(reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
@@ -4333,40 +2943,15 @@
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;
-
// 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);
@@ -4375,38 +2960,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);
@@ -4418,6 +3000,7 @@
moveOK = mService.mController.activityResuming(next.packageName);
} catch (RemoteException e) {
mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
}
if (!moveOK) {
return false;
@@ -4425,181 +3008,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;
@@ -4624,10 +3071,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;
@@ -4636,7 +3083,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,
@@ -4644,7 +3091,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;
@@ -4670,7 +3117,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"
@@ -4710,12 +3157,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
@@ -4730,7 +3177,7 @@
}
}
r.stopFreezingScreenLocked(false);
-
+
return true;
}
@@ -4748,9 +3195,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 ")
@@ -4768,9 +3215,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);
@@ -4779,8 +3223,303 @@
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;
+ }
+
+ // Remove this application's activities from active lists.
+ boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app);
+
+ if (!restarting) {
+ ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (stack == null) {
+ 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);
+ }
+ }
+ }
+ }
+
+ void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ pw.print(" Task "); pw.print(taskNdx); pw.print(": id #"); pw.println(task.taskId);
+ ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
+ " ", "Hist", true, !dumpAll, dumpClient, dumpPackage);
+ }
+ }
+
+ 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 moveTask(int taskId, boolean toTop) {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ return;
+ }
+ task.stack.mTaskHistory.remove(task);
+ 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..f7f0812
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -0,0 +1,2294 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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_RESULTS;
+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.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.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 class ActivityStackSupervisor {
+ static final boolean DEBUG_STACK = ActivityManagerService.DEBUG_STACK;
+
+ 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;
+
+ 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;
+
+ 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. */
+ 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 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>();
+
+ public ActivityStackSupervisor(ActivityManagerService service, Context context,
+ Looper looper) {
+ mService = service;
+ mContext = context;
+ mLooper = looper;
+ mHandler = new ActivityStackSupervisorHandler(looper);
+ }
+
+ 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 isFocusedStack(ActivityStack stack) {
+ return getFocusedStack() == stack;
+ }
+
+ boolean isFrontStack(ActivityStack stack) {
+ if (stack.mCurrentUser != mCurrentUser) {
+ return false;
+ }
+ return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
+ }
+
+ void moveHomeStack(boolean toFront) {
+ final boolean homeInFront = isFrontStack(mHomeStack);
+ if (homeInFront ^ toFront) {
+ mStackState = homeInFront ? STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT;
+ }
+ }
+
+ boolean resumeHomeActivity(ActivityRecord prev) {
+ moveHomeStack(true);
+ if (prev != null) {
+ prev.mLaunchHomeTaskNext = false;
+ }
+ if (mHomeStack.topRunningActivityLocked(null) != null) {
+ 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) {
+ final ActivityStack stack = task.stack;
+ 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 (mFocusedStack.mStackId == stackId) {
+ 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 ActivityRecord resumedActivity = mStacks.get(stackNdx).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:
+ mStackState = STACK_STATE_HOME_IN_BACK;
+ break;
+ case STACK_STATE_HOME_TO_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() {
+ ActivityRecord r = null;
+ if (mFocusedStack != null) {
+ r = mFocusedStack.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) {
+ continue;
+ }
+ if (stack != mFocusedStack && 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;
+ final int numStacks = mStacks.size();
+ 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 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 (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");
+ }
+
+ 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 (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);
+ 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) {
+ if (!r.isHomeActivity) {
+ 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);
+ mFocusedStack = getStack(stackId);
+ }
+ return mFocusedStack;
+ }
+ return mHomeStack;
+ }
+
+ void setFocusedStack(ActivityRecord r) {
+ if (r == null) {
+ return;
+ }
+ if (r.isHomeActivity) {
+ if (mStackState != STACK_STATE_HOME_IN_FRONT) {
+ mStackState = STACK_STATE_HOME_TO_FRONT;
+ }
+ } else {
+ mFocusedStack = r.task.stack;
+ if (mStackState != STACK_STATE_HOME_IN_BACK) {
+ 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.
+ ActivityStack lastStack = getLastStack();
+ targetStack = lastStack != null ? lastStack : mHomeStack;
+ moveHomeStack(targetStack.isHomeStack());
+ ActivityRecord prev = lastStack == null ? null : 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);
+ return ActivityManager.START_SUCCESS;
+ }
+
+ // Checked.
+ final ActivityRecord activityIdleInternalLocked(final 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<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();
+ res = r.task.stack.activityIdleInternalLocked(token);
+ if (res != null) {
+ 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 (allResumedActivitiesIdle()) {
+ mService.scheduleAppGcsLocked();
+ }
+ 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;
+ }
+ } 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();
+ }
+
+ 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 res;
+ }
+
+ 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)) {
+ 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 ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
+ return;
+ }
+ stack.moveTask(taskId, 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() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).stopIfSleepingLocked();
+ }
+ }
+
+ boolean shutdownLocked(int timeout) {
+ boolean timedout = false;
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.mResumedActivity != null) {
+ stack.stopIfSleepingLocked();
+ final long endTime = System.currentTimeMillis() + timeout;
+ while (stack.mResumedActivity != null || stack.mPausingActivity != null) {
+ long delay = endTime - System.currentTimeMillis();
+ if (delay <= 0) {
+ Slog.w(TAG, "Activity manager shutdown timed out");
+ timedout = true;
+ break;
+ }
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ return timedout;
+ }
+
+ void comeOutOfSleepIfNeededLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.awakeFromSleepingLocked();
+ if (isFrontStack(stack)) {
+ resumeTopActivitiesLocked();
+ }
+ }
+ }
+
+ boolean reportResumedActivityLocked(ActivityRecord r) {
+ final ActivityStack stack = r.task.stack;
+ if (isFrontStack(stack)) {
+ mService.updateUsageStats(r, true);
+ mService.setFocusedActivityLocked(r);
+ }
+ 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;
+ 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);
+ if (isFrontStack(stack)) {
+ if (r == null) {
+ Slog.e(TAG, "validateTop...: null top activity, stack=" + stack);
+ } else {
+ if (stack.mPausingActivity != null) {
+ Slog.e(TAG, "validateTop...: top stack has pausing activity r=" + r +
+ " state=" + r.state);
+ }
+ if (r.state != ActivityState.INITIALIZING &&
+ r.state != ActivityState.RESUMED) {
+ Slog.e(TAG, "validateTop...: activity in front not resumed r=" + r +
+ " state=" + r.state);
+ }
+ }
+ } else {
+ if (stack.mResumedActivity != null) {
+ Slog.e(TAG, "validateTop...: back stack has resumed activity r=" + r +
+ " state=" + r.state);
+ }
+ if (r != null && (r.state == ActivityState.INITIALIZING
+ || r.state == ActivityState.RESUMED)) {
+ Slog.e(TAG, "validateTop...: activity in back resumed r=" + r +
+ " state=" + r.state);
+ }
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity:");
+ pw.println(mDismissKeyguardOnNextActivity);
+ }
+
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ return getFocusedStack().getDumpActivitiesLocked(name);
+ }
+
+ boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ pw.print(" Stack #"); pw.print(mStacks.indexOf(stack)); pw.println(":");
+ stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage);
+ pw.println(" ");
+ pw.println(" Running activities (most recent first):");
+ dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, !dumpAll, false,
+ dumpPackage);
+ if (stack.mGoingToSleepActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting to sleep:");
+ dumpHistoryList(fd, pw, stack.mGoingToSleepActivities, " ", "Sleep", false,
+ !dumpAll, false, dumpPackage);
+ }
+
+ pw.print(" Stack #"); pw.println(mStacks.indexOf(stack));
+ if (stack.mPausingActivity != null) {
+ pw.println(" mPausingActivity: " + stack.mPausingActivity);
+ }
+ pw.println(" mResumedActivity: " + stack.mResumedActivity);
+ if (dumpAll) {
+ pw.println(" mLastPausedActivity: " + stack.mLastPausedActivity);
+ pw.println(" mSleepTimeout: " + stack.mSleepTimeout);
+ }
+ }
+
+ if (mFinishingActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting to finish:");
+ dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll, false,
+ dumpPackage);
+ }
+
+ if (mStoppingActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting to stop:");
+ dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll, false,
+ dumpPackage);
+ }
+
+ if (mWaitingVisibleActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting for another to become visible:");
+ dumpHistoryList(fd, pw, mWaitingVisibleActivities, " ", "Wait", false, !dumpAll,
+ false, dumpPackage);
+ }
+
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" mCurTaskId: " + mCurTaskId);
+ }
+ return true;
+ }
+
+ static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> 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 = 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;
+ }
+ }
+ }
+
+ 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);
+ }
+
+ 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: Callers=" +
+ Debug.getCallers(4));
+ 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: {
+ activityIdleInternal((ActivityRecord)msg.obj);
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivitiesLocked();
+ }
+ } 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;
+ supervisor.mStackState = mSavedStackState;
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index d19c7f6..9b3fbe9 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -470,8 +470,9 @@
private void dumpHelp(PrintWriter pw) {
pw.println("Battery stats (batteryinfo) dump options:");
- pw.println(" [--checkin] [--reset] [--write] [-h]");
+ pw.println(" [--checkin] [--unplugged] [--reset] [--write] [-h]");
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.");
@@ -488,11 +489,14 @@
}
boolean isCheckin = false;
+ boolean isUnpluggedOnly = false;
boolean noOutput = false;
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();
@@ -522,11 +526,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);
}
}
}
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 3a6492e..e697f88 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;
@@ -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,20 +294,8 @@
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--) {
@@ -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/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 8ab71dd..28593fe 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -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..c460791 100644
--- a/services/java/com/android/server/am/PendingThumbnailsRecord.java
+++ b/services/java/com/android/server/am/PendingThumbnailsRecord.java
@@ -27,13 +27,13 @@
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/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 1bae9ca..a01b882 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -16,13 +16,22 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityStack.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 int taskId; // Unique identifier for this task.
@@ -39,21 +48,30 @@
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;
+
+ 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 +122,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) {
+ // 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++;
+ }
+ mActivities.add(r);
+ }
+
+ void addActivityAtIndex(int index, ActivityRecord r) {
+ if (!mActivities.remove(r) && r.fullscreen) {
+ // Was not previously in list.
+ numFullscreen++;
+ }
+ 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) {
+ final ArrayList<ActivityRecord> activities = mActivities;
+ int numActivities = activities.size();
+ for ( ; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.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) {
+ final ArrayList<ActivityRecord> activities = mActivities;
+ int numActivities = activities.size();
+ for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.realActivity.equals(newR.realActivity)) {
+ // Here it is! Now finish everything in front...
+ ActivityRecord ret = r;
+
+ for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
+ r = activities.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) {
+ if (activities.contains(ret)) {
+ 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;
+ }
+
+ 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 +433,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 +444,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/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index c5b1c7b..cba8e0d 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;
@@ -31,43 +36,171 @@
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
*/
class UriPermission {
- final int uid;
+ 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..90ac88d 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -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/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 1c883ec..cf593ce 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,6 +72,7 @@
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.OnSyncRequestListener;
@@ -82,7 +81,6 @@
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;
@@ -381,10 +379,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
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 17b0662..ca85e42 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -37,6 +37,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import com.android.server.UiThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -190,12 +191,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);
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..f154ab3
--- /dev/null
+++ b/services/java/com/android/server/pm/KeySetManager.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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";
+
+ private static final long KEYSET_NOT_FOUND = -1;
+ 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.e(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.e(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 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 KEYSET_NOT_FOUND if the identifier doesn't identify a 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 KeySets that signed the given package.
+ *
+ * If the package is unknown to us, throws an IllegalArgumentException.
+ */
+ 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 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 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(packageName);
+ 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) {
+ synchronized (mLockObject) {
+ pw.println(" Dumping KeySetManager");
+ for (Map.Entry<String, PackageSetting> e : mPackages.entrySet()) {
+ String packageName = e.getKey();
+ PackageSetting pkg = e.getValue();
+ pw.print(" ["); pw.print(packageName); pw.println("]");
+ if (pkg.keySetData != null) {
+ pw.print(" Defined KeySets:");
+ for (long keySetId : pkg.keySetData.getDefinedKeySets()) {
+ pw.print(" "); pw.print(Long.toString(keySetId));
+ }
+ pw.println("");
+ pw.print(" Signing KeySets:");
+ for (long keySetId : pkg.keySetData.getSigningKeySets()) {
+ pw.print(" "); pw.print(Long.toString(keySetId));
+ }
+ 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);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Could not read KeySets for KeySetManager!");
+ }
+ }
+ }
+
+ 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"));
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Could not read keys for KeySetManager!");
+ }
+ }
+ }
+
+ 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);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Could not read KeySets for KeySetManager!");
+ }
+ }
+ }
+
+ 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) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Could not read public key for KeySetManager!");
+ } else {
+ mPublicKeys.put(identifier, pub);
+ }
+ }
+}
\ No newline at end of file
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 e804dfa..47282ba 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -70,6 +70,7 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
@@ -115,10 +116,12 @@
import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
+import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -3400,10 +3403,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
@@ -3550,6 +3555,7 @@
} else {
resPath = pkg.mScanPath;
}
+
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
@@ -4586,6 +4592,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;
@@ -8953,7 +8977,9 @@
removePackageDataLI(ps, 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
@@ -8966,6 +8992,7 @@
ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo,
writeSettings);
}
+
return ret;
}
@@ -9808,6 +9835,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;
@@ -9905,6 +9934,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);
@@ -9946,6 +9976,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);
}
}
@@ -10089,7 +10121,14 @@
}
}
}
-
+
+ if (dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println(" ");
+ }
+ mSettings.mKeySetManager.dump(pw);
+ }
+
if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
mSettings.dumpPackagesLPr(pw, packageName, dumpState);
}
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index e64ec6d..b3fd60c 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) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 2e48074..a9c2ea1 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;
@@ -119,6 +122,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 +182,9 @@
private final Context mContext;
private final File mSystemDir;
+
+ public final KeySetManager mKeySetManager = new KeySetManager(mPackages);
+
Settings(Context context) {
this(context, Environment.getDataDirectory());
}
@@ -729,6 +737,7 @@
} else {
mOtherUserIds.remove(uid);
}
+ setFirstAvailableUid(uid+1);
}
private void replaceUserIdLPw(int uid, Object obj) {
@@ -1331,6 +1340,8 @@
}
}
+ mKeySetManager.writeKeySetManagerLPr(serializer);
+
serializer.endTag(null, "packages");
serializer.endDocument();
@@ -1521,9 +1532,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 +1731,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());
@@ -2293,12 +2328,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.e(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 +2522,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;
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 1203e02..198851c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -50,6 +50,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;
@@ -364,8 +365,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);
@@ -2164,18 +2163,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
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index c7f7390..ba321bc 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -490,11 +490,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/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..3a5357a 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;
@@ -158,11 +157,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");
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..af7db96 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,8 +16,18 @@
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.graphics.Rect;
+import android.graphics.Region;
+import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.InputChannel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -61,12 +71,53 @@
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>();
+
+ /** Forward motion events to mTapDetector. */
+ InputChannel mTapInputChannel;
+
+ /** Detect user tapping outside of current focused stack bounds .*/
+ StackTapDetector 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 +143,269 @@
return mDisplayInfo;
}
+ 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 relativeStackId,
+ int position, float weight) {
+ TaskStack newStack = null;
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackId="
+ + relativeStackId + " 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(relativeStackId)) {
+ 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, relativeStackId, position, weight);
+ if (newStack != null) {
+ break;
+ }
+ }
+ }
+ if (stackBoxNdx < 0) {
+ throw new IllegalArgumentException("createStack: stackId " + relativeStackId
+ + " not found.");
+ }
+ }
+ if (newStack != null) {
+ layoutNeeded = true;
+ }
+ return newStack;
+ }
+
+ /** Refer to {@link WindowManagerService#resizeStack(int, float)} */
+ boolean resizeStack(int stackId, float weight) {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ final StackBox box = mStackBoxes.get(stackBoxNdx);
+ if (box.resize(stackId, weight)) {
+ 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);
+ }
+
+ /**
+ * 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.
+ */
+ void setStackBoxSize(Rect contentRect) {
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).setStackBoxSizes(contentRect);
+ }
+ }
+
+ 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 +429,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/StackBox.java b/services/java/com/android/server/wm/StackBox.java
new file mode 100644
index 0000000..2b43b40
--- /dev/null
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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}. To left of, lower l/r Rect values. */
+ public static final int TASK_STACK_GOES_BEFORE = 0;
+ /** Used with {@link WindowManagerService#createStack}. To right of, higher l/r Rect values. */
+ public static final int TASK_STACK_GOES_AFTER = 1;
+ /** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
+ public static final int TASK_STACK_GOES_ABOVE = 2;
+ /** Used with {@link WindowManagerService#createStack}. Vertical: higher t/b Rect values. */
+ public static final int TASK_STACK_GOES_BELOW = 3;
+ /** Used with {@link WindowManagerService#createStack}. Put on a higher layer on display. */
+ public static final int TASK_STACK_GOES_OVER = 4;
+ /** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
+ public static final int TASK_STACK_GOES_UNDER = 5;
+
+ /** 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;
+
+ /** 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) {
+ mService = service;
+ mDisplayContent = displayContent;
+ mParent = parent;
+ }
+
+ /** Propagate #layoutNeeded bottom up. */
+ void makeDirty() {
+ layoutNeeded = true;
+ if (mParent != null) {
+ mParent.makeDirty();
+ }
+ }
+
+ /**
+ * Determine if a particular TaskStack is in this StackBox or any of its descendants.
+ * @param stackId The TaskStack being considered.
+ * @return true if the specified TaskStack is in this box or its descendants. False otherwise.
+ */
+ boolean contains(int stackId) {
+ if (mStack != null) {
+ return mStack.mStackId == stackId;
+ }
+ return mFirst.contains(stackId) || mSecond.contains(stackId);
+ }
+
+ /**
+ * 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 relativeStackId The id of the TaskStack to place the new one 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 relativeStackId, int position, float weight) {
+ if (mStack == null) {
+ // Propagate the split to see if the target task stack is in either sub box.
+ TaskStack stack = mFirst.split(stackId, relativeStackId, position, weight);
+ if (stack != null) {
+ return stack;
+ }
+ return mSecond.split(stackId, relativeStackId, position, weight);
+ }
+
+ // This StackBox contains just a TaskStack.
+ if (mStack.mStackId != relativeStackId) {
+ // Barking down the wrong stack.
+ return null;
+ }
+
+ // Found it!
+ TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
+ TaskStack firstStack;
+ TaskStack secondStack;
+ switch (position) {
+ default:
+ case TASK_STACK_GOES_AFTER:
+ case TASK_STACK_GOES_BEFORE:
+ mVertical = false;
+ if (position == TASK_STACK_GOES_BEFORE) {
+ 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 stackId, float weight) {
+ if (mStack == null) {
+ return mFirst.resize(stackId, weight) || mSecond.resize(stackId, weight);
+ }
+ if (mStack.mStackId == stackId) {
+ mParent.mWeight = isFirstChild() ? weight : 1.0f - weight;
+ return true;
+ }
+ return false;
+ }
+
+ /** If this is a terminal StackBox (contains a TaskStack) set the bounds.
+ * @param bounds The rectangle to set the bounds to.
+ * @return True if the bounds changed, false otherwise. */
+ boolean setStackBoxSizes(Rect bounds) {
+ boolean change;
+ if (mStack != null) {
+ change = !mBounds.equals(bounds);
+ mBounds.set(bounds);
+ mStack.setBounds(bounds);
+ } 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);
+ mTmpRect.top = mTmpRect.bottom;
+ mTmpRect.bottom = bounds.top + height;
+ change |= mSecond.setStackBoxSizes(mTmpRect);
+ } else {
+ final int width = bounds.width();
+ int firstWidth = (int)(width * mWeight);
+ mTmpRect.right = bounds.left + firstWidth;
+ change = mFirst.setStackBoxSizes(mTmpRect);
+ mTmpRect.left = mTmpRect.right;
+ mTmpRect.right = bounds.left + width;
+ change |= mSecond.setStackBoxSizes(mTmpRect);
+ }
+ }
+ 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/StackTapDetector.java b/services/java/com/android/server/wm/StackTapDetector.java
new file mode 100644
index 0000000..a71b075
--- /dev/null
+++ b/services/java/com/android/server/wm/StackTapDetector.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.server.wm;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Looper;
+import android.view.DisplayInfo;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+
+import com.android.server.wm.WindowManagerService.H;
+
+public class StackTapDetector extends InputEventReceiver {
+ 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 StackTapDetector(WindowManagerService service, DisplayContent displayContent,
+ InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ mService = service;
+ mDisplayContent = displayContent;
+ mTouchExcludeRegion = displayContent.mTouchExcludeRegion;
+ DisplayInfo info = displayContent.getDisplayInfo();
+ mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)
+ || !event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent)event;
+ 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);
+ synchronized (this) {
+ 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/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..50d23a1
--- /dev/null
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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) {
+ mStackBox.makeDirty();
+ mTasks.add(toTop ? mTasks.size() : 0, task);
+ task.mStack = this;
+ return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
+ }
+
+ boolean moveTaskToTop(Task task) {
+ mTasks.remove(task);
+ return addTask(task, true);
+ }
+
+ boolean 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) {
+ 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) {
+ mDimLayer.setBounds(bounds);
+ mAnimationBackgroundSurface.setBounds(bounds);
+ }
+
+ 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..9d6576a 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;
@@ -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,12 @@
}
}
- testTokenMayBeDrawnLocked();
-
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
- final ScreenRotationAnimation screenRotationAnimation =
- displayAnimator.mScreenRotationAnimation;
- if (screenRotationAnimation != null) {
- screenRotationAnimation.updateSurfacesInTransaction();
- }
+ testTokenMayBeDrawnLocked(displayId);
- 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 +521,8 @@
}
}
+ mService.setFocusedStackLayer();
+
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
@@ -661,26 +569,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 +606,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);
@@ -787,26 +663,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 +681,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..e22e70c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -16,29 +16,9 @@
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;
@@ -54,6 +34,7 @@
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;
@@ -200,6 +181,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 +192,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 +220,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 +267,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;
@@ -284,6 +281,8 @@
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String TAP_INPUT_CHANNEL_NAME = "StackTapDetector";
+
private static final int MAX_SCREENSHOT_RETRIES = 3;
final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -337,34 +336,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 +409,9 @@
final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
+ FocusedStackFrame mFocusedStackFrame;
+
+ int mFocusedStackLayer;
final float[] mTmpFloats = new float[9];
@@ -449,9 +424,11 @@
String mLastANRState;
- /** All DisplayDontents in the world, kept here */
+ /** All DisplayContents in the world, kept here */
private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>();
+ private final AllWindowsIterator mTmpWindowsIterator = new AllWindowsIterator();
+
int mRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mAltOrientation = false;
@@ -589,7 +566,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;
@@ -631,6 +607,9 @@
final WindowAnimator mAnimator;
+ SparseArray<Task> mTaskIdToTask = new SparseArray<Task>();
+ SparseArray<TaskStack> mStackIdToStack = new SparseArray<TaskStack>();
+
final class DragInputEventReceiver extends InputEventReceiver {
public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
@@ -722,8 +701,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 +709,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 +731,6 @@
private WindowManagerService(Context context, PowerManagerService pm,
DisplayManagerService displayManager, InputManagerService inputManager,
- Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -761,6 +738,7 @@
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);
@@ -812,11 +790,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 +801,8 @@
SurfaceControl.openTransaction();
try {
createWatermarkInTransaction();
+ mFocusedStackFrame = new FocusedStackFrame(
+ getDefaultDisplayContentLocked().getDisplay(), mFxSession);
} finally {
SurfaceControl.closeTransaction();
}
@@ -860,10 +839,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 +903,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 +1571,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) : "??")
@@ -1849,6 +1870,25 @@
}
}
+ 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="
@@ -2223,7 +2263,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 +2354,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 +2406,6 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
- mInputMonitor.updateInputWindowsLw(false /*force*/);
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -2495,22 +2534,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 +3189,71 @@
// 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="
+ + displayContent.getTasks());
+ 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 +3314,7 @@
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
+ DisplayContent displayContent = null;
WindowToken wtoken = mTokenMap.remove(token);
if (wtoken != null) {
boolean delayed = false;
@@ -3255,6 +3324,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 +3334,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 +3352,7 @@
}
if (delayed) {
- mExitingTokens.add(wtoken);
+ displayContent.mExitingTokens.add(wtoken);
} else if (wtoken.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(wtoken);
}
@@ -3297,27 +3366,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 +3396,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 +3435,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 +3490,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);
-
- 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;
+ // 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 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 (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;
+ }
+ }
+
+ // 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 == 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);
}
-
- // 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 (!haveGroup) {
- haveGroup = true;
- curGroup = atoken.groupId;
- 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 (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -3531,9 +3608,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 +3711,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,
@@ -3657,6 +3781,7 @@
}
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
+ moveTaskToTop(newFocus.groupId);
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp
+ " moveFocusNow=" + moveFocusNow);
if (changed) {
@@ -4346,11 +4471,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 +4487,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,78 +4542,91 @@
}
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--;
}
return 0;
@@ -4533,198 +4675,210 @@
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 moveTaskWindowsLocked(Task task) {
+ DisplayContent displayContent = task.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);
- }
+ AppTokenList tokens = task.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);
- if (newPos != pos) {
- displayContent.layoutNeeded = true;
- }
- pos = newPos;
+ // Where to start adding?
+ int pos = findAppWindowInsertionPointLocked(tokens.get(0));
+ 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;
}
- }
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignLayersLocked(windows);
+ pos = newPos;
}
}
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(displayContent.getWindowList());
+ }
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
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);
+ moveTaskWindowsLocked(task);
}
-
- 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;
+ }
+ task.mStack.moveTaskToBottom(task);
+ moveTaskWindowsLocked(task);
+ task.getDisplayContent().moveStack(task.mStack, false);
}
- 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 relativeStackId 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 relativeStackId, 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);
+ }
+ final DisplayContent displayContent;
+ if (stackId != HOME_STACK_ID) {
+ // TODO: What to do for the first stack on a non-default display?
+ final TaskStack relativeStack = mStackIdToStack.get(relativeStackId);
+ if (relativeStack == null) {
+ throw new IllegalArgumentException("createStack: Invalid relativeStackId=" +
+ relativeStackId);
+ }
+ displayContent = relativeStack.getDisplayContent();
+ } else {
+ displayContent = getDefaultDisplayContentLocked();
+ }
+ TaskStack stack =
+ displayContent.createStack(this, stackId, relativeStackId, position, weight);
+ mStackIdToStack.put(stackId, stack);
+ displayContent.moveStack(stack, true);
+ }
+ }
+
+ 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;
+ performLayoutAndPlaceSurfacesLocked();
+ return nextStackId;
+ }
+ if (DEBUG_STACK) Slog.i(TAG, "removeStack: could not find stackId=" + stackId);
+ }
+ return HOME_STACK_ID;
+ }
+
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ return;
+ }
+ task.mStack.removeTask(task);
+
+ TaskStack stack = mStackIdToStack.get(stackId);
+ stack.addTask(task, toTop);
+ stack.getDisplayContent().layoutNeeded = true;
+
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+
+ public void resizeStack(int stackId, 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) {
+ Task task = null;
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().resizeStack(stackId, weight)) {
+ break;
+ } else if (!iterator.hasNext()) {
+ throw new IllegalArgumentException("resizeStack: stackId " + stackId
+ + " not found.");
}
}
-
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
- moveAppWindowsLocked(tokens, 0);
}
- Binder.restoreCallingIdentity(origId);
+ }
+
+ 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 +5000,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);
@@ -4980,22 +5134,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();
}
@@ -5242,9 +5390,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;
@@ -5499,14 +5647,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 +5848,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 +5869,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 +6377,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;
}
@@ -6767,6 +6917,8 @@
displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
+ displayContent.mBaseDisplayRect.set(0, 0,
+ displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
}
}
}
@@ -6779,10 +6931,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 +6976,7 @@
public static final int DO_DISPLAY_CHANGED = 29;
public static final int CLIENT_FREEZE_TIMEOUT = 30;
+ public static final int TAP_OUTSIDE_STACK = 31;
@Override
public void handleMessage(Message msg) {
@@ -7068,8 +7221,6 @@
if (mAppTransition.isTransitionSet()) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** APP TRANSITION TIMEOUT");
mAppTransition.setTimeout();
- mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -7114,13 +7265,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 +7396,19 @@
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) {
+ }
+ }
+ }
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -7654,8 +7821,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,21 +7842,28 @@
// 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) {
@@ -7704,7 +7877,7 @@
}
}
Slog.w(TAG, "Current app token list:");
- dumpAnimatingAppTokensLocked();
+ dumpAppTokensLocked();
Slog.w(TAG, "Final window list:");
dumpWindowsLocked();
}
@@ -7741,13 +7914,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 +7934,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 +8080,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 +8533,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;
@@ -8509,30 +8693,35 @@
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 (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ "allDrawn: " + wtoken
+ + " interesting=" + numInteresting
+ + " drawn=" + wtoken.numDrawnWindows);
+ wtoken.allDrawn = true;
+ }
}
}
}
@@ -8556,13 +8745,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 +8784,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 +8867,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 +8885,7 @@
handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
}
- if (!mInnerFields.mDimming) {
+ if (!w.getStack().testDimmingTag()) {
handleFlagDimBehind(w, innerDw, innerDh);
}
@@ -8835,13 +9028,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) {
@@ -9012,30 +9203,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 +9254,7 @@
defaultDisplay.layoutNeeded = true;
}
- DisplayContentsIterator iterator = new DisplayContentsIterator();
+ iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
DisplayContent displayContent = iterator.next();
if (displayContent.pendingLayoutChanges != 0) {
@@ -9126,6 +9325,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 +9436,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 +9481,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 +9518,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 +9554,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;
}
@@ -9405,7 +9616,6 @@
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);
@@ -9471,8 +9681,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 +9710,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 +9720,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 +9746,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 +9860,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 +10130,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 +10145,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 +10189,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 +10384,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 +10401,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);
@@ -10437,7 +10614,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 +10627,15 @@
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) {
+ InputChannel inputChannel = monitorInput(TAP_INPUT_CHANNEL_NAME);
+ displayContent.mTapInputChannel = inputChannel;
+ displayContent.mTapDetector =
+ new StackTapDetector(this, displayContent, inputChannel, Looper.myLooper());
+ }
+
return displayContent;
}
@@ -10479,6 +10666,10 @@
class DisplayContentsIterator implements Iterator<DisplayContent> {
private int cur;
+ void reset() {
+ cur = 0;
+ }
+
@Override
public boolean hasNext() {
return cur < mDisplayContents.size();
@@ -10498,7 +10689,6 @@
}
}
- final static boolean REVERSE_ITERATOR = true;
class AllWindowsIterator implements Iterator<WindowState> {
private DisplayContent mDisplayContent;
private DisplayContentsIterator mDisplayContentsIterator;
@@ -10507,19 +10697,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 +10815,11 @@
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
mDisplayContents.delete(displayId);
+
+ if (displayContent.mTapInputChannel != null) {
+ displayContent.mTapInputChannel.dispose();
+ }
+
WindowList windows = displayContent.getWindowList();
while (!windows.isEmpty()) {
final WindowState win = windows.get(windows.size() - 1);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index dfb22a7..c45d786 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -57,6 +57,12 @@
import java.util.ArrayList;
class WindowList extends ArrayList<WindowState> {
+ WindowList() {
+ super();
+ }
+ WindowList(WindowList windows) {
+ super(windows);
+ }
}
/**
@@ -434,14 +440,17 @@
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
mHaveFrame = true;
- final Rect container = mContainingFrame;
- container.set(pf);
+ final int type = mAttrs.type;
+ if (mAppToken != null) {
+ mContainingFrame.set(getStackBounds());
+ } 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) {
@@ -488,18 +497,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);
@@ -513,59 +516,57 @@
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(mOverscanFrame.left - mFrame.left,
+ mOverscanFrame.top - mFrame.top,
+ mFrame.right - mOverscanFrame.right,
+ mFrame.bottom - mOverscanFrame.bottom);
- 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);
@@ -578,8 +579,8 @@
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ "): frame=" + mFrame.toShortString()
- + " ci=" + contentInsets.toShortString()
- + " vi=" + visibleInsets.toShortString());
+ + " ci=" + mContentInsets.toShortString()
+ + " vi=" + mVisibleInsets.toShortString());
//}
}
}
@@ -682,6 +683,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
@@ -876,7 +896,8 @@
|| (atoken == null && mRootToken.hidden)
|| (atoken != null && (atoken.hiddenRequested || atoken.hidden))
|| mAttachedHidden
- || mExiting || mDestroying;
+ || (mExiting && !isAnimatingLw())
+ || mDestroying;
}
/**
@@ -1113,7 +1134,7 @@
return true;
}
- public boolean setAppOpVisibilityLw(boolean state) {
+ public void setAppOpVisibilityLw(boolean state) {
if (mAppOpVisibility != state) {
mAppOpVisibility = state;
if (state) {
@@ -1123,13 +1144,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..8bf7788 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -19,7 +19,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;
@@ -1185,7 +1184,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..e416676 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 \
@@ -43,7 +44,11 @@
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 485c289..0c8b4a5 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -144,7 +144,7 @@
static int readFromFile(const String8& path, char* buf, size_t size)
{
- if (!path)
+ if (path.isEmpty())
return -1;
int fd = open(path.string(), O_RDONLY, 0);
if (fd == -1) {
@@ -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/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/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..8cd36bf
--- /dev/null
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -0,0 +1,211 @@
+<?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>
+
+ </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/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/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/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/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..e251d5c
--- /dev/null
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?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>
+</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..3bb7100
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.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.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 {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Fade fader;
+ 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..2fcdc30
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.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.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.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/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/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/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/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..10baac5 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) {
@@ -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..61180a0 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,35 @@
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 += -DSTATIC_ANDROIDFW_FOR_TOOLS
+LOCAL_CFLAGS += -Wno-non-virtual-dtor
+
+LOCAL_STATIC_LIBRARIES := \
+ libstlport_static \
+ libandroidfw \
+ libutils \
+ libcutils \
+ libexpat_static \
+ libpng \
+ liblog \
+ libz
+
+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..0ca230c 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -87,8 +87,8 @@
// ---- 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 +211,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
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/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 6e3034b..4bee937 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -85,6 +85,7 @@
private static final String ENGINE_KEY = "engine";
private static final String ENGINE_ID_KEY = "engine_id";
private static final String PRIVATE_KEY_ID_KEY = "key_id";
+ private static final String OPP_KEY_CACHING = "proactive_key_caching";
private HashMap<String, String> mFields = new HashMap<String, String>();
private X509Certificate mCaCert;
@@ -273,6 +274,7 @@
case Eap.TLS:
case Eap.TTLS:
mFields.put(EAP_KEY, Eap.strings[eapMethod]);
+ mFields.put(OPP_KEY_CACHING, "1");
break;
default:
throw new IllegalArgumentException("Unknown EAP method");
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 9cae2cb..995ef50 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -3598,6 +3598,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: