Merge "add settings for dock audio enabled" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index 31ed755..e26d8f0 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -10093,8 +10093,10 @@
public final class DisplayManager {
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
+ method public android.view.Display[] getDisplays(java.lang.String);
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+ field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
}
public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+ method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@
method public java.lang.CharSequence getName(android.content.Context);
method public int getPlaybackStream();
method public int getPlaybackType();
+ method public android.view.Display getPresentationDisplay();
method public java.lang.CharSequence getStatus();
method public int getSupportedTypes();
method public java.lang.Object getTag();
@@ -16619,6 +16623,7 @@
public class UserManager {
method public long getSerialNumberForUser(android.os.UserHandle);
+ method public int getUserCount();
method public android.os.UserHandle getUserForSerialNumber(long);
method public java.lang.String getUserName();
method public boolean isUserAGoat();
@@ -23742,6 +23747,7 @@
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
@@ -24788,6 +24794,7 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
method public boolean gatherTransparentRegion(android.graphics.Region);
method public android.view.SurfaceHolder getHolder();
+ method public void setSecure(boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
}
@@ -25362,7 +25369,6 @@
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
- field public static int TEXT_ALIGNMENT_DEFAULT;
field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25370,7 +25376,6 @@
field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
- field public static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/api/current.txt b/api/current.txt
index 31ed755..e26d8f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10093,8 +10093,10 @@
public final class DisplayManager {
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
+ method public android.view.Display[] getDisplays(java.lang.String);
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+ field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
}
public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+ method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@
method public java.lang.CharSequence getName(android.content.Context);
method public int getPlaybackStream();
method public int getPlaybackType();
+ method public android.view.Display getPresentationDisplay();
method public java.lang.CharSequence getStatus();
method public int getSupportedTypes();
method public java.lang.Object getTag();
@@ -16619,6 +16623,7 @@
public class UserManager {
method public long getSerialNumberForUser(android.os.UserHandle);
+ method public int getUserCount();
method public android.os.UserHandle getUserForSerialNumber(long);
method public java.lang.String getUserName();
method public boolean isUserAGoat();
@@ -23742,6 +23747,7 @@
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
@@ -24788,6 +24794,7 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
method public boolean gatherTransparentRegion(android.graphics.Region);
method public android.view.SurfaceHolder getHolder();
+ method public void setSecure(boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
}
@@ -25362,7 +25369,6 @@
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
- field public static int TEXT_ALIGNMENT_DEFAULT;
field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25370,7 +25376,6 @@
field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
- field public static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index 20b27c5..3e8af60 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -50,6 +50,93 @@
* whenever the activity itself is paused or resumed.
* </p>
*
+ * <h3>Choosing a presentation display</h3>
+ * <p>
+ * Before showing a {@link Presentation} it's important to choose the {@link Display}
+ * on which it will appear. Choosing a presentation display is sometimes difficult
+ * because there may be multiple displays attached. Rather than trying to guess
+ * which display is best, an application should let the system choose a suitable
+ * presentation display.
+ * </p><p>
+ * There are two main ways to choose a {@link Display}.
+ * </p>
+ *
+ * <h4>Using the media router to choose a presentation display</h4>
+ * <p>
+ * The easiest way to choose a presentation display is to use the
+ * {@link android.media.MediaRouter MediaRouter} API. The media router service keeps
+ * track of which audio and video routes are available on the system.
+ * The media router sends notifications whenever routes are selected or unselected
+ * or when the preferred presentation display of a route changes.
+ * So an application can simply watch for these notifications and show or dismiss
+ * a presentation on the preferred presentation display automatically.
+ * </p><p>
+ * The preferred presentation display is the display that the media router recommends
+ * that the application should use if it wants to show content on the secondary display.
+ * Sometimes there may not be a preferred presentation display in which
+ * case the application should show its content locally without using a presentation.
+ * </p><p>
+ * Here's how to use the media router to create and show a presentation on the preferred
+ * presentation display using {@link android.media.MediaRouter.RouteInfo#getPresentationDisplay()}.
+ * </p>
+ * {@samplecode
+ * MediaRouter mediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
+ * if (route != null) ${
+ * Display presentationDisplay = route.getPresentationDisplay();
+ * if (presentationDisplay != null) ${
+ * Presentation presentation = new MyPresentation(context, presentationDisplay);
+ * presentation.show();
+ * $}
+ * $}
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the media
+ * router to automatically switch between showing content in the main activity and showing
+ * the content in a presentation when a presentation display is available.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
+ * activity}
+ *
+ * <h4>Using the display manager to choose a presentation display</h4>
+ * <p>
+ * Another way to choose a presentation display is to use the {@link DisplayManager} API
+ * directly. The display manager service provides functions to enumerate and describe all
+ * displays that are attached to the system including displays that may be used
+ * for presentations.
+ * </p><p>
+ * The display manager keeps track of all displays in the system. However, not all
+ * displays are appropriate for showing presentations. For example, if an activity
+ * attempted to show a presentation on the main display it might obscure its own content
+ * (it's like opening a dialog on top of your activity).
+ * </p><p>
+ * Here's how to identify suitable displays for showing presentations using
+ * {@link DisplayManager#getDisplays(String)} and the
+ * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION} category.
+ * </p>
+ * {@samplecode
+ * DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ * Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ * if (presentationDisplays.length > 0) ${
+ * // If there is more than one suitable presentation display, then we could consider
+ * // giving the user a choice. For this example, we simply choose the first display
+ * // which is the one the system recommends as the preferred presentation display.
+ * Display display = presentationDisplays[0];
+ * Presentation presentation = new MyPresentation(context, presentationDisplay);
+ * presentation.show();
+ * $}
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the display
+ * manager to enumerate displays and show content on multiple presentation displays
+ * simultaneously.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
+ * activity}
+ *
+ * @see android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO for information on about live
+ * video routes and how to obtain the preferred presentation display for the
+ * current media route.
* @see DisplayManager for information on how to enumerate displays and receive
* notifications when displays are added or removed.
*/
@@ -121,7 +208,7 @@
@Override
protected void onStart() {
super.onStart();
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
// Since we were not watching for display changes until just now, there is a
// chance that the display metrics have changed. If so, we will need to
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 8cbf5b1..26bde19 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
index 163e4e2..d5e64f6 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothHealth.aidl b/core/java/android/bluetooth/IBluetoothHealth.aidl
index e741da4..a84a42c 100644
--- a/core/java/android/bluetooth/IBluetoothHealth.aidl
+++ b/core/java/android/bluetooth/IBluetoothHealth.aidl
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl
index 23e6d50..1ebb9ca 100755
--- a/core/java/android/bluetooth/IBluetoothInputDevice.aidl
+++ b/core/java/android/bluetooth/IBluetoothInputDevice.aidl
@@ -1,6 +1,19 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
import android.bluetooth.BluetoothDevice;
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index de8fe91..ed8777c 100755
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
index 3e795ea..9551086 100644
--- a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothPan.aidl b/core/java/android/bluetooth/IBluetoothPan.aidl
index b91bd7d..5a32347 100644
--- a/core/java/android/bluetooth/IBluetoothPan.aidl
+++ b/core/java/android/bluetooth/IBluetoothPan.aidl
@@ -1,6 +1,19 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.bluetooth;
import android.bluetooth.BluetoothDevice;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 28e320b..0a7a2e7 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -21,6 +21,8 @@
import android.util.SparseArray;
import android.view.Display;
+import java.util.ArrayList;
+
/**
* Manages the properties of attached displays.
* <p>
@@ -40,6 +42,8 @@
private final Object mLock = new Object();
private final SparseArray<Display> mDisplays = new SparseArray<Display>();
+ private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
+
/**
* Broadcast receiver that indicates when the Wifi display status changes.
* <p>
@@ -60,6 +64,20 @@
public static final String EXTRA_WIFI_DISPLAY_STATUS =
"android.hardware.display.extra.WIFI_DISPLAY_STATUS";
+ /**
+ * Display category: Presentation displays.
+ * <p>
+ * This category can be used to identify secondary displays that are suitable for
+ * use as presentation displays.
+ * </p>
+ *
+ * @see android.app.Presentation for information about presenting content
+ * on secondary displays.
+ * @see #getDisplays(String)
+ */
+ public static final String DISPLAY_CATEGORY_PRESENTATION =
+ "android.hardware.display.category.PRESENTATION";
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -87,24 +105,52 @@
* @return An array containing all displays.
*/
public Display[] getDisplays() {
- int[] displayIds = mGlobal.getDisplayIds();
- int expectedCount = displayIds.length;
- Display[] displays = new Display[expectedCount];
+ return getDisplays(null);
+ }
+
+ /**
+ * Gets all currently valid logical displays of the specified category.
+ * <p>
+ * When there are multiple displays in a category the returned displays are sorted
+ * of preference. For example, if the requested category is
+ * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
+ * then the displays are sorted so that the first display in the returned array
+ * is the most preferred presentation display. The application may simply
+ * use the first display or allow the user to choose.
+ * </p>
+ *
+ * @param category The requested display category or null to return all displays.
+ * @return An array containing all displays sorted by order of preference.
+ *
+ * @see #DISPLAY_CATEGORY_PRESENTATION
+ */
+ public Display[] getDisplays(String category) {
+ final int[] displayIds = mGlobal.getDisplayIds();
synchronized (mLock) {
- int actualCount = 0;
- for (int i = 0; i < expectedCount; i++) {
- Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
- if (display != null) {
- displays[actualCount++] = display;
+ try {
+ if (category == null) {
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, -1);
+ } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
}
- }
- if (actualCount != expectedCount) {
- Display[] oldDisplays = displays;
- displays = new Display[actualCount];
- System.arraycopy(oldDisplays, 0, displays, 0, actualCount);
+ return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
+ } finally {
+ mTempDisplays.clear();
}
}
- return displays;
+ }
+
+ private void addMatchingDisplaysLocked(
+ ArrayList<Display> displays, int[] displayIds, int matchType) {
+ for (int i = 0; i < displayIds.length; i++) {
+ Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
+ if (display != null
+ && (matchType < 0 || display.getType() == matchType)) {
+ displays.add(display);
+ }
+ }
}
private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 898c766..d73f99a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -122,7 +122,7 @@
* @param userHandle the user handle of the user whose information is being requested.
* @return the UserInfo object for a specific user.
* @hide
- * */
+ */
public UserInfo getUserInfo(int userHandle) {
try {
return mService.getUserInfo(userHandle);
@@ -134,10 +134,11 @@
/**
* Return the serial number for a user. This is a device-unique
- * number assigned to that user; if the user is deleted and new users
- * created, the new users will not be given the same serial number.
+ * number assigned to that user; if the user is deleted and then a new
+ * user created, the new users will not be given the same serial number.
* @param user The user whose serial number is to be retrieved.
- * @return The serial number of the given user.
+ * @return The serial number of the given user; returns -1 if the
+ * given UserHandle does not exist.
* @see #getUserForSerialNumber(long)
*/
public long getSerialNumberForUser(UserHandle user) {
@@ -179,6 +180,14 @@
}
/**
+ * Return the number of users currently created on the device.
+ */
+ public int getUserCount() {
+ List<UserInfo> users = getUsers();
+ return users != null ? users.size() : 1;
+ }
+
+ /**
* Returns information for all users on this device.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @return the list of users that were created.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e26c51a..23c12f6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3222,18 +3222,11 @@
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
/**
- * Id of the time appwidget on the lockscreen, or -1 if none
- * @hide
- */
- public static final String LOCK_SCREEN_STATUS_APPWIDGET_ID =
- "lock_screen_status_appwidget_id";
-
- /**
* Id of the user-selected appwidget on the lockscreen, or -1 if none
* @hide
*/
- public static final String LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID =
- "lock_screen_user_selected_appwidget_id";
+ public static final String LOCK_SCREEN_APPWIDGET_IDS =
+ "lock_screen_appwidget_ids";
/**
* This preference enables showing the owner info on LockScren.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 662dc45..758abb5 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -54,7 +54,9 @@
private final DisplayManagerGlobal mGlobal;
private final int mDisplayId;
private final int mLayerStack;
- private final String mName;
+ private final int mFlags;
+ private final int mType;
+ private final String mAddress;
private final CompatibilityInfoHolder mCompatibilityInfo;
private DisplayInfo mDisplayInfo; // never null
@@ -82,22 +84,95 @@
* Display flag: Indicates that the display supports compositing content
* that is stored in protected graphics buffers.
* <p>
+ * If this flag is set then the display device supports compositing protected buffers.
+ * </p><p>
+ * If this flag is not set then the display device may not support compositing
+ * protected buffers; the user may see a blank region on the screen instead of
+ * the protected content.
+ * </p><p>
* Secure (DRM) video decoders may allocate protected graphics buffers to request that
* a hardware-protected path be provided between the video decoder and the external
* display sink. If a hardware-protected path is not available, then content stored
* in protected graphics buffers may not be composited.
* </p><p>
- * If this flag is not set, then the display device does not support compositing
- * protected buffers; the user may see a blank region on the screen instead of
- * the protected content. An application can use this flag as a hint that it should
- * select an alternate content stream or adopt a different strategy for decoding
- * content that does not rely on protected buffers so as to ensure that the user
- * can view the content on the display as expected.
+ * An application can use the absence of this flag as a hint that it should not use protected
+ * buffers for this display because the content may not be visible. For example,
+ * if the flag is not set then the application may choose not to show content on this
+ * display, show an informative error message, select an alternate content stream
+ * or adopt a different strategy for decoding content that does not rely on
+ * protected buffers.
* </p>
+ *
+ * @see #getFlags
*/
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 0;
/**
+ * Display flag: Indicates that the display has a secure video output and
+ * supports compositing secure surfaces.
+ * <p>
+ * If this flag is set then the display device has a secure video output
+ * and is capable of showing secure surfaces. It may also be capable of
+ * showing {@link #FLAG_SUPPORTS_PROTECTED_BUFFERS protected buffers}.
+ * </p><p>
+ * If this flag is not set then the display device may not have a secure video
+ * output; the user may see a blank region on the screen instead of
+ * the contents of secure surfaces or protected buffers.
+ * </p><p>
+ * Secure surfaces are used to prevent content rendered into those surfaces
+ * by applications from appearing in screenshots or from being viewed
+ * on non-secure displays. Protected buffers are used by secure video decoders
+ * for a similar purpose.
+ * </p><p>
+ * An application creates a window with a secure surface by specifying the
+ * {@link WindowManager.LayoutParams#FLAG_SECURE} window flag.
+ * Likewise, an application creates a {@link SurfaceView} with a secure surface
+ * by calling {@link SurfaceView#setSecure} before attaching the secure view to
+ * its containing window.
+ * </p><p>
+ * An application can use the absence of this flag as a hint that it should not create
+ * secure surfaces or protected buffers on this display because the content may
+ * not be visible. For example, if the flag is not set then the application may
+ * choose not to show content on this display, show an informative error message,
+ * select an alternate content stream or adopt a different strategy for decoding
+ * content that does not rely on secure surfaces or protected buffers.
+ * </p>
+ *
+ * @see #getFlags
+ */
+ public static final int FLAG_SECURE = 1 << 1;
+
+ /**
+ * Display type: Unknown display type.
+ * @hide
+ */
+ public static final int TYPE_UNKNOWN = 0;
+
+ /**
+ * Display type: Built-in display.
+ * @hide
+ */
+ public static final int TYPE_BUILT_IN = 1;
+
+ /**
+ * Display type: HDMI display.
+ * @hide
+ */
+ public static final int TYPE_HDMI = 2;
+
+ /**
+ * Display type: WiFi display.
+ * @hide
+ */
+ public static final int TYPE_WIFI = 3;
+
+ /**
+ * Display type: Overlay display.
+ * @hide
+ */
+ public static final int TYPE_OVERLAY = 4;
+
+ /**
* Internal method to create a display.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
* or {@link android.hardware.display.DisplayManager#getDisplay}
@@ -111,10 +186,14 @@
mGlobal = global;
mDisplayId = displayId;
mDisplayInfo = displayInfo;
- mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
- mName = displayInfo.name; // cannot change as long as the display is valid
mCompatibilityInfo = compatibilityInfo;
mIsValid = true;
+
+ // Cache properties that cannot change as long as the display is valid.
+ mLayerStack = displayInfo.layerStack;
+ mFlags = displayInfo.flags;
+ mType = displayInfo.type;
+ mAddress = displayInfo.address;
}
/**
@@ -182,12 +261,37 @@
* @return The display flags.
*
* @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
+ * @see #FLAG_SECURE
*/
public int getFlags() {
- synchronized (this) {
- updateDisplayInfoLocked();
- return mDisplayInfo.flags;
- }
+ return mFlags;
+ }
+
+ /**
+ * Gets the display type.
+ *
+ * @return The display type.
+ *
+ * @see #TYPE_UNKNOWN
+ * @see #TYPE_BUILT_IN
+ * @see #TYPE_HDMI
+ * @see #TYPE_WIFI
+ * @see #TYPE_OVERLAY
+ * @hide
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the display address, or null if none.
+ * Interpretation varies by display type.
+ *
+ * @return The display address.
+ * @hide
+ */
+ public String getAddress() {
+ return mAddress;
}
/**
@@ -202,10 +306,17 @@
/**
* Gets the name of the display.
+ * <p>
+ * Note that some displays may be renamed by the user.
+ * </p>
+ *
* @return The display's name.
*/
public String getName() {
- return mName;
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.name;
+ }
}
/**
@@ -483,5 +594,25 @@
+ ", " + mTempMetrics + ", isValid=" + mIsValid;
}
}
+
+ /**
+ * @hide
+ */
+ public static String typeToString(int type) {
+ switch (type) {
+ case TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case TYPE_BUILT_IN:
+ return "BUILT_IN";
+ case TYPE_HDMI:
+ return "HDMI";
+ case TYPE_WIFI:
+ return "WIFI";
+ case TYPE_OVERLAY:
+ return "OVERLAY";
+ default:
+ return Integer.toString(type);
+ }
+ }
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index fb04150..f3841d5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -39,6 +39,17 @@
public int flags;
/**
+ * Display type.
+ */
+ public int type;
+
+ /**
+ * Display address, or null if none.
+ * Interpretation varies by display type.
+ */
+ public String address;
+
+ /**
* The human-readable name of the display.
*/
public String name;
@@ -143,10 +154,12 @@
public float physicalYDpi;
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
+ @Override
public DisplayInfo createFromParcel(Parcel source) {
return new DisplayInfo(source);
}
+ @Override
public DisplayInfo[] newArray(int size) {
return new DisplayInfo[size];
}
@@ -171,6 +184,9 @@
public boolean equals(DisplayInfo other) {
return other != null
&& layerStack == other.layerStack
+ && flags == other.flags
+ && type == other.type
+ && Objects.equal(address, other.address)
&& Objects.equal(name, other.name)
&& appWidth == other.appWidth
&& appHeight == other.appHeight
@@ -195,6 +211,8 @@
public void copyFrom(DisplayInfo other) {
layerStack = other.layerStack;
flags = other.flags;
+ type = other.type;
+ address = other.address;
name = other.name;
appWidth = other.appWidth;
appHeight = other.appHeight;
@@ -214,6 +232,8 @@
public void readFromParcel(Parcel source) {
layerStack = source.readInt();
flags = source.readInt();
+ type = source.readInt();
+ address = source.readString();
name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
@@ -234,6 +254,8 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(layerStack);
dest.writeInt(this.flags);
+ dest.writeInt(type);
+ dest.writeString(address);
dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
@@ -294,11 +316,17 @@
+ ", rotation " + rotation
+ ", density " + logicalDensityDpi
+ ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
- + ", layerStack " + layerStack + flagsToString(flags) + "}";
+ + ", layerStack " + layerStack
+ + ", type " + Display.typeToString(type)
+ + ", address " + address
+ + flagsToString(flags) + "}";
}
private static String flagsToString(int flags) {
StringBuilder result = new StringBuilder();
+ if ((flags & Display.FLAG_SECURE) != 0) {
+ result.append(", FLAG_SECURE");
+ }
if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 59f941d..1c613245 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1526,30 +1526,6 @@
}
@Override
- void destroyLayers(View view) {
- if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) {
- if (mCanvas != null) {
- mCanvas.clearLayerUpdates();
- }
- destroyHardwareLayer(view);
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
- }
- }
-
- private static void destroyHardwareLayer(View view) {
- view.destroyLayer(true);
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyHardwareLayer(group.getChildAt(i));
- }
- }
- }
-
- @Override
boolean safelyRun(Runnable action) {
boolean needsContext = true;
if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
@@ -1574,6 +1550,35 @@
}
@Override
+ void destroyLayers(final View view) {
+ if (view != null) {
+ safelyRun(new Runnable() {
+ @Override
+ public void run() {
+ if (mCanvas != null) {
+ mCanvas.clearLayerUpdates();
+ }
+ destroyHardwareLayer(view);
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ }
+ });
+ }
+ }
+
+ private static void destroyHardwareLayer(View view) {
+ view.destroyLayer(true);
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+
+ int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ destroyHardwareLayer(group.getChildAt(i));
+ }
+ }
+ }
+
+ @Override
void destroyHardwareResources(final View view) {
if (view != null) {
safelyRun(new Runnable() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0d16dd3..9008521 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -385,7 +385,27 @@
mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
}
-
+
+ /**
+ * Control whether the surface view's content should be treated as secure,
+ * preventing it from appearing in screenshots or from being viewed on
+ * non-secure displays.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>See {@link android.view.Display#FLAG_SECURE} for details.
+ *
+ * @param isSecure True if the surface view is secure.
+ */
+ public void setSecure(boolean isSecure) {
+ if (isSecure) {
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+ } else {
+ mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
+ }
+ }
+
/**
* Hack to allow special layering of windows. The type is one of the
* types in WindowManager.LayoutParams. This is a hack so:
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5e259e..9d0d4f0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1864,7 +1864,6 @@
/**
* Default horizontal layout direction.
- * @hide
*/
private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
@@ -1914,7 +1913,7 @@
/**
* Default text direction is inherited
*/
- public static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+ private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
/**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
@@ -2024,7 +2023,7 @@
/**
* Default text alignment is inherited
*/
- public static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+ private static final int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
/**
* Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
@@ -3224,7 +3223,7 @@
mContext = context;
mResources = context != null ? context.getResources() : null;
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
- // Set layout and text direction defaults
+ // Set some flags defaults
mPrivateFlags2 =
(LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) |
(TEXT_DIRECTION_DEFAULT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT) |
@@ -14198,11 +14197,13 @@
* @hide
*/
protected void resolveDrawables() {
- if (mBackground != null) {
- mBackground.setLayoutDirection(getLayoutDirection());
+ if (canResolveLayoutDirection()) {
+ if (mBackground != null) {
+ mBackground.setLayoutDirection(getLayoutDirection());
+ }
+ mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
+ onResolveDrawables(getLayoutDirection());
}
- mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
- onResolveDrawables(getLayoutDirection());
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c252c77..00723f3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3384,7 +3384,6 @@
if (child.isLayoutDirectionInherited()) {
child.resetRtlProperties();
- child.resolveRtlPropertiesIfNeeded();
}
onViewAdded(child);
@@ -4817,8 +4816,6 @@
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
- final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 01923e2..3b31ff6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -628,8 +628,13 @@
@Deprecated
public static final int FLAG_DITHER = 0x00001000;
- /** Window flag: don't allow screen shots while this window is
- * displayed. Maps to Surface.SECURE. */
+ /** Window flag: Treat the content of the window as secure, preventing
+ * it from appearing in screenshots or from being viewed on non-secure
+ * displays.
+ *
+ * <p>See {@link android.view.Display#FLAG_SECURE} for more details about
+ * secure surfaces and secure displays.
+ */
public static final int FLAG_SECURE = 0x00002000;
/** Window flag: a special mode where the layout parameters are used
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 19b825c..495e46b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -144,6 +144,8 @@
CharSequence mError;
boolean mErrorWasChanged;
ErrorPopup mErrorPopup;
+ private int mLastLayoutDirection = -1;
+
/**
* This flag is set if the TextView tries to display an error before it
* is attached to the window (so its position is still unknown).
@@ -288,23 +290,30 @@
public void setError(CharSequence error, Drawable icon) {
mError = TextUtils.stringOrSpannedString(error);
mErrorWasChanged = true;
- final Drawables dr = mTextView.mDrawables;
- if (dr != null) {
- switch (mTextView.getLayoutDirection()) {
+ final int layoutDirection = mTextView.getLayoutDirection();
+ if (mLastLayoutDirection != layoutDirection) {
+ final Drawables dr = mTextView.mDrawables;
+ switch (layoutDirection) {
default:
case View.LAYOUT_DIRECTION_LTR:
- mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
- dr.mDrawableBottom);
+ if (dr != null) {
+ mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+ dr.mDrawableBottom);
+ } else {
+ mTextView.setCompoundDrawables(null, null, icon, null);
+ }
break;
case View.LAYOUT_DIRECTION_RTL:
- mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
- dr.mDrawableBottom);
+ if (dr != null) {
+ mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
+ dr.mDrawableBottom);
+ } else {
+ mTextView.setCompoundDrawables(icon, null, null, null);
+ }
break;
}
- } else {
- mTextView.setCompoundDrawables(null, null, icon, null);
+ mLastLayoutDirection = layoutDirection;
}
-
if (mError == null) {
if (mErrorPopup != null) {
if (mErrorPopup.isShowing()) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e158776..738f63b 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -304,16 +304,11 @@
int maxWidth = 0;
int childState = 0;
- final int layoutDirection = getLayoutDirection();
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 83c15bb..b6f0862 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -648,8 +648,6 @@
int largestChildHeight = Integer.MIN_VALUE;
- final int layoutDirection = getLayoutDirection();
-
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -669,7 +667,6 @@
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
@@ -992,8 +989,6 @@
int largestChildWidth = Integer.MIN_VALUE;
- final int layoutDirection = getLayoutDirection();
-
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1014,7 +1009,6 @@
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index ace26f3..e52e84d 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -414,15 +414,12 @@
final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
- final int layoutDirection = getLayoutDirection();
-
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.resolveLayoutDirection(layoutDirection);
applyHorizontalSizeRules(params, myWidth);
measureChildHorizontal(child, params, myWidth, myHeight);
@@ -486,6 +483,8 @@
}
}
+ final int layoutDirection = getLayoutDirection();
+
if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
@@ -730,7 +729,6 @@
final int layoutDirection = getLayoutDirection();
int[] rules = params.getRules(layoutDirection);
- params.resolveLayoutDirection(layoutDirection);
if (params.mLeft < 0 && params.mRight >= 0) {
// Right is fixed, but left varies
@@ -984,7 +982,6 @@
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams st =
(RelativeLayout.LayoutParams) child.getLayoutParams();
- st.resolveLayoutDirection(getLayoutDirection());
child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1def89f..bc41931 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -329,13 +329,11 @@
return;
}
- final int layoutDirection = getLayoutDirection();
if (getChildCount() > 0) {
final View child = getChildAt(0);
int height = getMeasuredHeight();
if (child.getMeasuredHeight() < height) {
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 68ffd73..35927e0 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -192,9 +192,7 @@
int widthMeasureSpec, int totalWidth,
int heightMeasureSpec, int totalHeight) {
if (mConstrainedColumnWidths != null) {
- final int layoutDirection = getLayoutDirection();
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
int measureMode = MeasureSpec.EXACTLY;
int columnWidth = 0;
@@ -228,6 +226,7 @@
final int childWidth = child.getMeasuredWidth();
lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth;
+ final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
@@ -293,13 +292,11 @@
}
final int[] columnWidths = mColumnWidths;
- final int layoutDirection = getLayoutDirection();
for (int i = 0; i < numColumns; i++) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
- layoutParams.resolveLayoutDirection(layoutDirection);
if (layoutParams.span == 1) {
int spec;
switch (layoutParams.width) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8e5ff40..a46481c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -306,7 +306,7 @@
private Layout.Alignment mLayoutAlignment;
private int mResolvedTextAlignment;
- private boolean mResolvedDrawables;
+ private int mLastLayoutDirection = -1;
/**
* On some devices the fading edges add a performance penalty if used
@@ -8260,16 +8260,16 @@
@Override
public void onResolveDrawables(int layoutDirection) {
// No need to resolve twice
- if (mResolvedDrawables) {
+ if (mLastLayoutDirection == layoutDirection) {
return;
}
+ mLastLayoutDirection = layoutDirection;
// No drawable to resolve
if (mDrawables == null) {
return;
}
// No relative drawable to resolve
if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
- mResolvedDrawables = true;
return;
}
@@ -8307,7 +8307,6 @@
break;
}
updateDrawablesLayoutDirection(dr, layoutDirection);
- mResolvedDrawables = true;
}
private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
@@ -8329,7 +8328,7 @@
* @hide
*/
protected void resetResolvedDrawables() {
- mResolvedDrawables = false;
+ mLastLayoutDirection = -1;
}
/**
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 43c63b6..8bc1081 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -343,11 +343,9 @@
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
- final int layoutDirection = getLayoutDirection();
if (mClose != null) {
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
availableWidth -= lp.leftMargin + lp.rightMargin;
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d8b3d2f..6fb459c 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -946,9 +946,6 @@
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
- final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
-
int horizontalMargin = 0;
int verticalMargin = 0;
if (ablp != null) {
@@ -1099,9 +1096,8 @@
customView = mCustomNavView;
}
if (customView != null) {
- ViewGroup.LayoutParams lp = customView.getLayoutParams();
final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
+ ViewGroup.LayoutParams lp = customView.getLayoutParams();
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
@@ -1339,15 +1335,11 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
height = Math.max(height,
@@ -1387,12 +1379,10 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int vCenter = (b - t) / 2;
final boolean isLayoutRtl = isLayoutRtl();
- final int layoutDirection = getLayoutDirection();
final int width = getWidth();
int upOffset = 0;
if (mUpView.getVisibility() != GONE) {
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
- upLp.resolveLayoutDirection(layoutDirection);
final int upHeight = mUpView.getMeasuredHeight();
final int upWidth = mUpView.getMeasuredWidth();
upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
@@ -1413,7 +1403,6 @@
}
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
- iconLp.resolveLayoutDirection(layoutDirection);
final int iconHeight = mIconView.getMeasuredHeight();
final int iconWidth = mIconView.getMeasuredWidth();
final int hCenter = (r - l) / 2;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3f40f20..f6ae83c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,10 +16,6 @@
package com.android.internal.widget;
-import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
-import com.google.android.collect.Lists;
-
import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
@@ -29,7 +25,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -43,6 +38,10 @@
import android.view.View;
import android.widget.Button;
+import com.android.internal.R;
+import com.android.internal.telephony.ITelephony;
+import com.google.android.collect.Lists;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -125,6 +124,11 @@
*/
public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
+ /**
+ * Pseudo-appwidget id we use to represent the default clock status widget
+ */
+ public static final int ID_DEFAULT_STATUS_WIDGET = -2;
+
protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
@@ -146,6 +150,7 @@
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
private ILockSettings mLockSettingsService;
+ private int mStickyWidgetIndex = -1;
// The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
private static volatile int sCurrentUserId = UserHandle.USER_NULL;
@@ -1045,28 +1050,116 @@
}
}
- public int[] getUserDefinedWidgets() {
- int appWidgetId = -1;
+ public int[] getAppWidgets() {
String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID,
+ mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
UserHandle.USER_CURRENT);
- if (appWidgetIdString != null) {
- appWidgetId = (int) Integer.decode(appWidgetIdString);
+ String delims = ",";
+ if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
+ String[] appWidgetStringIds = appWidgetIdString.split(delims);
+ int[] appWidgetIds = new int[appWidgetStringIds.length];
+ for (int i = 0; i < appWidgetStringIds.length; i++) {
+ String appWidget = appWidgetStringIds[i];
+ try {
+ appWidgetIds[i] = Integer.decode(appWidget);
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Error when parsing widget id " + appWidget);
+ return null;
+ }
+ }
+ return appWidgetIds;
}
-
- return new int[] { appWidgetId };
+ if (appWidgetIdString == null) {
+ return new int[] { LockPatternUtils.ID_DEFAULT_STATUS_WIDGET };
+ } else {
+ return new int[0];
+ }
}
- public int getStatusWidget() {
- int appWidgetId = -1;
- String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_STATUS_APPWIDGET_ID,
- UserHandle.USER_CURRENT);
- if (appWidgetIdString != null) {
- appWidgetId = (int) Integer.decode(appWidgetIdString);
+ private static String combineStrings(int[] list, String separator) {
+ int listLength = list.length;
+
+ switch (listLength) {
+ case 0: {
+ return "";
+ }
+ case 1: {
+ return Integer.toString(list[0]);
+ }
}
- return appWidgetId;
+ int strLength = 0;
+ int separatorLength = separator.length();
+
+ String[] stringList = new String[list.length];
+ for (int i = 0; i < listLength; i++) {
+ stringList[i] = Integer.toString(list[i]);
+ strLength += stringList[i].length();
+ if (i < listLength - 1) {
+ strLength += separatorLength;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder(strLength);
+
+ for (int i = 0; i < listLength; i++) {
+ sb.append(list[i]);
+ if (i < listLength - 1) {
+ sb.append(separator);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private void writeAppWidgets(int[] appWidgetIds) {
+ Settings.Secure.putStringForUser(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
+ combineStrings(appWidgetIds, ","),
+ UserHandle.USER_CURRENT);
+ }
+
+ // TODO: log an error if this returns false
+ public boolean addAppWidget(int widgetId, int index) {
+ int[] widgets = getAppWidgets();
+ if (widgets == null) {
+ return false;
+ }
+ if (index < 0 || index > widgets.length) {
+ return false;
+ }
+ int[] newWidgets = new int[widgets.length + 1];
+ for (int i = 0, j = 0; i < newWidgets.length; i++) {
+ if (index == i) {
+ newWidgets[i] = widgetId;
+ i++;
+ }
+ if (i < newWidgets.length) {
+ newWidgets[i] = widgets[j];
+ j++;
+ }
+ }
+ writeAppWidgets(newWidgets);
+ return true;
+ }
+
+ public boolean removeAppWidget(int widgetId) {
+ int[] widgets = getAppWidgets();
+
+ int[] newWidgets = new int[widgets.length - 1];
+ for (int i = 0, j = 0; i < widgets.length; i++) {
+ if (widgets[i] == widgetId) {
+ // continue...
+ } else if (j >= newWidgets.length) {
+ // we couldn't find the widget
+ return false;
+ } else {
+ newWidgets[j] = widgets[i];
+ j++;
+ }
+ }
+ writeAppWidgets(newWidgets);
+ return true;
}
private long getLong(String secureSettingKey, long defaultValue) {
@@ -1218,4 +1311,12 @@
return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
}
+ public int getStickyWidgetIndex() {
+ return mStickyWidgetIndex;
+ }
+
+ public void setStickyWidgetIndex(int stickyWidgetIndex) {
+ mStickyWidgetIndex = stickyWidgetIndex;
+ }
+
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 0f49776..b7f64ec 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -256,11 +256,8 @@
setDirectionDescriptionsResourceId(resourceId);
}
- a.recycle();
+ mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
- // Use gravity attribute from LinearLayout
- a = context.obtainStyledAttributes(attrs, android.R.styleable.LinearLayout);
- mGravity = a.getInt(android.R.styleable.LinearLayout_gravity, Gravity.TOP);
a.recycle();
setVibrateEnabled(mVibrationDuration > 0);
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
index 7d5516a..6293432 100644
--- a/core/res/res/anim/keyguard_security_fade_in.xml
+++ b/core/res/res/anim/keyguard_security_fade_in.xml
@@ -15,8 +15,8 @@
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@interpolator/decelerate_quad"
+ android:interpolator="@android:interpolator/decelerate_quad"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@integer/kg_security_fade_duration" />
+ 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
index 08c8b2b..4ab0229 100644
--- a/core/res/res/anim/keyguard_security_fade_out.xml
+++ b/core/res/res/anim/keyguard_security_fade_out.xml
@@ -13,9 +13,9 @@
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="@interpolator/accelerate_quad"
+<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"
+ android:duration="@*android:integer/kg_security_fade_duration"
/>
diff --git a/core/res/res/drawable-hdpi/add_widget.png b/core/res/res/drawable-hdpi/add_widget.png
new file mode 100644
index 0000000..fb64a52
--- /dev/null
+++ b/core/res/res/drawable-hdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..c34fe20
--- /dev/null
+++ b/core/res/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/core/res/res/drawable-hdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..fb1c866
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock.png b/core/res/res/drawable-hdpi/kg_security_lock.png
new file mode 100644
index 0000000..136d3ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/security_frame.9.png b/core/res/res/drawable-hdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/security_handle.png b/core/res/res/drawable-hdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..f1bcf48
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/add_widget.png b/core/res/res/drawable-mdpi/add_widget.png
new file mode 100644
index 0000000..ae26787
--- /dev/null
+++ b/core/res/res/drawable-mdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..f636524
--- /dev/null
+++ b/core/res/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/core/res/res/drawable-mdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..25beb2b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock.png b/core/res/res/drawable-mdpi/kg_security_lock.png
new file mode 100644
index 0000000..861760d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/security_frame.9.png b/core/res/res/drawable-mdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/security_handle.png b/core/res/res/drawable-mdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..d5a7708
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..f1bcf48
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..d5a7708
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..55174e0
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/add_widget.png b/core/res/res/drawable-xhdpi/add_widget.png
new file mode 100644
index 0000000..c02700c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/default_wallpaper.jpg b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
index 5b8d1d5..da9fa91 100644
--- a/core/res/res/drawable-xhdpi/default_wallpaper.jpg
+++ b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..9c4a603
--- /dev/null
+++ b/core/res/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/core/res/res/drawable-xhdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..b5cd134
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock.png b/core/res/res/drawable-xhdpi/kg_security_lock.png
new file mode 100644
index 0000000..4544584
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/security_frame.9.png b/core/res/res/drawable-xhdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/security_handle.png b/core/res/res/drawable-xhdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..55174e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index 521853f..bb455bd 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -21,30 +21,53 @@
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_vertical"
android:orientation="horizontal">
- <include layout="@layout/keyguard_widget_region"
- android:layout_width="0dp"
+ <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="@integer/kg_widget_region_weight" />
+ android:clipChildren="false">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="@integer/kg_security_flipper_weight"
- 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">
+ <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"/>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ <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="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-land/keyguard_widget_pager.xml b/core/res/res/layout-land/keyguard_widget_pager.xml
new file mode 100644
index 0000000..8b7b9a3
--- /dev/null
+++ b/core/res/res/layout-land/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.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:clipChildren="false"
+ android:clipToPadding="false"
+ androidprv:pageSpacing="10dp">
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel>
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
index 981fe6d..15e9844 100644
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -21,28 +21,57 @@
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">
- <include layout="@layout/keyguard_widget_region"
+ <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout
+ android:id="@+id/sliding_layout"
android:layout_width="match_parent"
- android:layout_height="153dp" />
+ android:layout_height="match_parent"
+ androidprv:dragHandle="@drawable/kg_security_grip"
+ androidprv:dragIcon="@drawable/kg_security_lock">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_weight="1"
- 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>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <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"
+ androidprv:layout_childType="challenge"
+ android:layout_marginLeft="@dimen/kg_edge_swipe_region_size"
+ android:layout_marginRight="@dimen/kg_edge_swipe_region_size"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:gravity="bottom|center_horizontal">
+ <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.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
new file mode 100644
index 0000000..6662f83
--- /dev/null
+++ b/core/res/res/layout-port/keyguard_widget_pager.xml
@@ -0,0 +1,32 @@
+<?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="25dp"
+ android:clipChildren="false"
+ 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
index 23c1e9c..3953c95 100644
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -21,33 +21,54 @@
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">
+ android:orientation="horizontal">
- <include layout="@layout/keyguard_widget_region"
+ <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="@integer/kg_widget_region_weight" />
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="@integer/kg_security_flipper_weight"
+ 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:layout_gravity="center">
+ android:orientation="vertical">
+ <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"/>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ <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"
+ 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/keyguard_glow_pad_container.xml b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
index 1eef099..930b14e 100644
--- a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
+++ b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
@@ -18,7 +18,7 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/keyguard_glow_pad_view"
- android:layout_width="@dimen/kg_glow_pad_size"
- android:layout_height="@dimen/kg_glow_pad_size"
+ 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/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml
new file mode 100644
index 0000000..fa811d7
--- /dev/null
+++ b/core/res/res/layout/keyguard_add_widget.xml
@@ -0,0 +1,40 @@
+<?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"
+ >
+ <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/add_widget" />
+ </FrameLayout>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml b/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
index 68840ab..eeb4178 100644
--- a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
+++ b/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
@@ -61,7 +61,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:drawableLeft="@drawable/lockscreen_forgot_password_button"
+ 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"
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
index 845c265..00f14f5 100644
--- a/core/res/res/layout/keyguard_face_unlock_view.xml
+++ b/core/res/res/layout/keyguard_face_unlock_view.xml
@@ -39,7 +39,7 @@
android:id="@+id/spotlightMask"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/facelock_spotlight_mask"
+ android:background="@*android:color/facelock_spotlight_mask"
/>
<ImageButton
@@ -50,7 +50,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:background="#00000000"
- android:src="@drawable/ic_facial_backup"
+ android:src="@*android:drawable/ic_facial_backup"
/>
</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
index 0e851e3..2d8f02d 100644
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -20,35 +20,26 @@
<!-- 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="125dp"
- android:layout_height="125dp"
- android:background="#000000"
+ 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="centerCrop"
+ android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.78" />
- <TextView
- android:id="@+id/keyguard_user_name"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.22"
- android:paddingLeft="6dp"
- android:layout_gravity="center_vertical|left"
- android:textSize="16sp"
- android:textColor="#ffffff"
- android:singleLine="true"
- android:ellipsize="end"
- android:background="#808080" />
- </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
\ No newline at end of file
+ <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
index 5a6e998..ee01285 100644
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -17,18 +17,23 @@
*/
-->
<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="match_parent"
- android:layout_gravity="center"
- android:contentDescription="@string/keyguard_accessibility_user_selector">
+ 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.KeyguardSubdivisionLayout
+ <com.android.internal.policy.impl.keyguard.KeyguardLinearLayout
android:id="@+id/keyguard_users_grid"
android:orientation="horizontal"
- android:layout_width="300dp"
- android:layout_height="300dp"
- android:layout_gravity="center" />
+ 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>
\ No newline at end of file
+</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
index ad9fdfe..fc126fe 100644
--- a/core/res/res/layout/keyguard_multi_user_selector_widget.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
@@ -23,7 +23,4 @@
android:id="@+id/keyguard_multi_user_selector"
android:layout_width="match_parent"
android:layout_height="match_parent">
-
- <include layout="@layout/keyguard_multi_user_selector"/>
-
</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
index 81916f7..e28f2ac 100644
--- a/core/res/res/layout/keyguard_password_view.xml
+++ b/core/res/res/layout/keyguard_password_view.xml
@@ -77,18 +77,6 @@
android:imeOptions="flagForceAscii|actionDone"
/>
- <!-- This delete button is only visible for numeric PIN entry -->
- <ImageButton android:id="@+id/delete_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:src="@*android:drawable/ic_input_delete"
- android:clickable="true"
- android:padding="8dip"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
<ImageView android:id="@+id/switch_ime_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -101,20 +89,6 @@
/>
</LinearLayout>
-
- <!-- Numeric keyboard -->
- <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="4dip"
- android:layout_marginEnd="4dip"
- android:paddingTop="4dip"
- android:paddingBottom="4dip"
- android:background="#40000000"
- android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
- android:visibility="gone"
- android:clickable="true"
- />
</LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..9b883af
--- /dev/null
+++ b/core/res/res/layout/keyguard_pin_view.xml
@@ -0,0 +1,206 @@
+<?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="350dp"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/passwordEntry"
+ 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="@android: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"
+ />
+ </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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ 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/passwordEntry"
+ androidprv:digit="0"
+ />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@android:style/Widget.Button.NumPadKey"
+ android:gravity="center"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:src="@drawable/sym_keyboard_return_holo"
+ />
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_emergency_carrier_area_and_recovery"
+ 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_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 1de0f6c..9532a88 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -35,7 +35,7 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="center_horizontal|top"
android:orientation="vertical">
<com.android.internal.policy.impl.keyguard.ClockView
android:id="@+id/clock_view"
diff --git a/core/res/res/layout/keyguard_transport_control_view.xml b/core/res/res/layout/keyguard_transport_control_view.xml
index 5a6083a..532322c 100644
--- a/core/res/res/layout/keyguard_transport_control_view.xml
+++ b/core/res/res/layout/keyguard_transport_control_view.xml
@@ -26,8 +26,8 @@
<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">
+ 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"
@@ -70,11 +70,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:src="@drawable/ic_media_previous"
+ android:src="@*android:drawable/ic_media_previous"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_prev_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_prev_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -86,10 +86,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
- android:src="@drawable/ic_media_play"
+ android:src="@*android:drawable/ic_media_play"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_play_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_play_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -101,10 +101,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
- android:src="@drawable/ic_media_next"
+ android:src="@*android:drawable/ic_media_next"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_next_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_next_description"/>
</FrameLayout>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_widget_region.xml b/core/res/res/layout/keyguard_widget_region.xml
deleted file mode 100644
index ed10c2b..0000000
--- a/core/res/res/layout/keyguard_widget_region.xml
+++ /dev/null
@@ -1,71 +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.KeyguardWidgetRegion
- xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/kg_widget_region"
- android:visibility="gone"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:clipChildren="false"
- android:clipToPadding="false">
- </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="10dp"
- android:orientation="horizontal"
- android:paddingLeft="@dimen/kg_widget_pager_horizontal_padding"
- android:paddingRight="@dimen/kg_widget_pager_horizontal_padding"
- android:layout_marginTop="@dimen/kg_runway_lights_top_margin"
- android:visibility="gone">
- <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
- android:id="@+id/left_strip"
- android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
- android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- prvandroid:numDots="5"
- prvandroid:dotSize="@dimen/kg_runway_lights_height"
- prvandroid:leftToRight="false"
- prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1.6"/>
- <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
- android:id="@+id/right_strip"
- android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
- android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- prvandroid:numDots="5"
- prvandroid:dotSize="@dimen/kg_runway_lights_height"
- prvandroid:leftToRight="true"
- prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
- </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index fdd35a1..d63f85d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Allows the app to write new words into the user dictionary."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"test access to protected storage"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"test access to protected storage"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Allows the app to test a permission for USB storage that will be availabe on future devices."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Allows the app to test a permission for USB storage that will be available on future devices."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Allows the app to test a permission for the SD card that will be available on future devices."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a333187..9389579 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1381,12 +1381,9 @@
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Pantalla HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición #<xliff:g id="ID">%1$d</xliff:g>"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> x <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> ppp"</string>
- <!-- no translation found for wifi_display_notification_title (2223050649240326557) -->
- <skip />
- <!-- no translation found for wifi_display_notification_message (4498802012464170685) -->
- <skip />
- <!-- no translation found for wifi_display_notification_disconnect (6183754463561153372) -->
- <skip />
+ <string name="wifi_display_notification_title" msgid="2223050649240326557">"Se conectó la pantalla inalámbrica"</string>
+ <string name="wifi_display_notification_message" msgid="4498802012464170685">"Esta pantalla se muestra en otro dispositivo."</string>
+ <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Desconectar"</string>
<string name="kg_emergency_call_label" msgid="684946192523830531">"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>
diff --git a/core/res/res/values-land/bools.xml b/core/res/res/values-land/bools.xml
index b0630ad..85c64d9 100644
--- a/core/res/res/values-land/bools.xml
+++ b/core/res/res/values-land/bools.xml
@@ -15,6 +15,7 @@
-->
<resources>
+ <bool name="kg_enable_camera_default_widget">false</bool>
<bool name="kg_share_status_area">false</bool>
<bool name="kg_sim_puk_account_full_screen">false</bool>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4d24fcb..855f461 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1382,7 +1382,7 @@
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наложение № <xliff:g id="ID">%1$d</xliff:g>"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> х <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> тчк/дюйм"</string>
<string name="wifi_display_notification_title" msgid="2223050649240326557">"Беспроводной проектор подключен"</string>
- <string name="wifi_display_notification_message" msgid="4498802012464170685">"Изображение проецируется на другое устройство"</string>
+ <string name="wifi_display_notification_message" msgid="4498802012464170685">"Изображение передается на другое устройство"</string>
<string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Отключить"</string>
<string name="kg_emergency_call_label" msgid="684946192523830531">"Экстренный вызов"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забыли графический ключ?"</string>
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 3753aba..eae4f87 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -19,4 +19,6 @@
<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>
+ <!-- No camera for you, tablet user -->
+ <bool name="kg_enable_camera_default_widget">false</bool>
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 564545a..0d01df4 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -108,5 +108,9 @@
<dimen name="kg_runway_lights_top_margin">-10dp</dimen>
<!-- Margin around the various security views -->
- <dimen name="keyguard_security_view_margin">24dp</dimen>
+ <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/core/res/res/values-sw600dp/integers.xml b/core/res/res/values-sw600dp/integers.xml
new file mode 100644
index 0000000..de9829c
--- /dev/null
+++ b/core/res/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/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 6144961..d6d2b66 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -101,5 +101,15 @@
<dimen name="kg_runway_lights_top_margin">-30dp</dimen>
<!-- Margin around the various security views -->
- <dimen name="keyguard_security_view_margin">100dp</dimen>
+ <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>
+
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 8783b0b..b0c363bc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1381,8 +1381,8 @@
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI 屏幕"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"叠加视图 #<xliff:g id="ID">%1$d</xliff:g>"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>:<xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>,<xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
- <string name="wifi_display_notification_title" msgid="2223050649240326557">"无线显示设备已连接"</string>
- <string name="wifi_display_notification_message" msgid="4498802012464170685">"此屏幕显示在另一台设备上"</string>
+ <string name="wifi_display_notification_title" msgid="2223050649240326557">"已连接到无线显示设备"</string>
+ <string name="wifi_display_notification_message" msgid="4498802012464170685">"此屏幕的内容正显示在另一台设备上"</string>
<string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"断开连接"</string>
<string name="kg_emergency_call_label" msgid="684946192523830531">"紧急呼救"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘记了图案"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 8615476..8744bfe 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -398,4 +398,17 @@
<item>@null</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 3550df9..48d4745 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5464,6 +5464,8 @@
<!-- Used when the handle shouldn't wait to be hit before following the finger -->
<attr name="alwaysTrackFinger"/>
+
+ <attr name="gravity" />
</declare-styleable>
<!-- =============================== -->
@@ -5759,14 +5761,6 @@
<!-- PagedView specific attributes. These attributes are used to customize
a PagedView view in XML files. -->
<declare-styleable name="PagedView">
- <!-- A spacing override for the icons within a page -->
- <attr name="pageLayoutWidthGap" format="dimension" />
- <attr name="pageLayoutHeightGap" format="dimension" />
- <!-- The padding of the pages that are dynamically created per page -->
- <attr name="pageLayoutPaddingTop" format="dimension" />
- <attr name="pageLayoutPaddingBottom" format="dimension" />
- <attr name="pageLayoutPaddingLeft" format="dimension" />
- <attr name="pageLayoutPaddingRight" format="dimension" />
<!-- The space between adjacent pages of the PagedView. -->
<attr name="pageSpacing" format="dimension" />
<!-- The padding for the scroll indicator area -->
@@ -5781,10 +5775,58 @@
<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" />
+ </attr>
+
+ <declare-styleable name="SlidingChallengeLayout">
+ <attr name="dragHandle" format="reference" />
+ <attr name="dragIcon" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="SlidingChallengeLayout_Layout">
+ <attr name="layout_childType" />
+ </declare-styleable>
+
<!-- Attributes that can be used with <code><FragmentBreadCrumbs></code>
tags. -->
<declare-styleable name="FragmentBreadCrumbs">
<attr name="gravity" />
</declare-styleable>
+ <declare-styleable name="MultiPaneChallengeLayout">
+ <!-- Influences how layout_centerWithinArea behaves -->
+ <attr name="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="NumPadKey">
+ <attr name="digit" format="integer" />
+ <attr name="textView" format="reference" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index f9762b1..d4ead01 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -15,6 +15,7 @@
-->
<resources>
+ <bool name="kg_enable_camera_default_widget">true</bool>
<bool name="action_bar_embed_tabs">true</bool>
<bool name="action_bar_embed_tabs_pre_jb">false</bool>
<bool name="split_action_bar_is_narrow">true</bool>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 6a93f30..b19e23d 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -190,5 +190,10 @@
<drawable name="notification_template_icon_bg">#3333B5E5</drawable>
<drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
+ <!-- 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>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 948a3d3..8a7632c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -242,8 +242,8 @@
<dimen name="notification_subtext_size">12dp</dimen>
<!-- Keyguard dimensions -->
- <!-- Width of security view in keyguard. -->
- <dimen name="kg_glow_pad_size">500dp</dimen>
+ <!-- TEMP -->
+ <dimen name="kg_security_panel_height">600dp</dimen>
<!-- Height of security view in keyguard. -->
<dimen name="kg_security_view_height">0dp</dimen>
@@ -305,4 +305,23 @@
<!-- 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>
</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 6d49a91..91ad5d8 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -17,6 +17,7 @@
*/
-->
<resources>
+ <integer name="kg_carousel_angle">75</integer>
<integer name="kg_security_flip_duration">75</integer>
<integer name="kg_security_fade_duration">75</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f8dbd84..47a8fc5 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1721,7 +1721,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_sdcardRead" product="default">test access to protected 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=30] -->
- <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be availabe on future devices. </string>
+ <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be available on future devices. </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_sdcardRead" product="default">Allows the app to test a permission for the SD card that will be available on future devices.</string>
@@ -3950,5 +3950,7 @@
<string name="enable_accessibility_canceled">Accessibility canceled.</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>
+ <!-- Default name of the owner user [CHAR LIMIT=20] -->
+ <string name="owner_name" msgid="3879126011135546571">Owner</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4371aec..da26e6a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -2478,4 +2478,29 @@
<item name="android:contentDescription">@android:string/media_route_button_content_description</item>
</style>
+ <!-- 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>
+ </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>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8ef91df..60a2e91 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -842,6 +842,7 @@
<java-symbol type="string" name="media_route_status_connecting" />
<java-symbol type="string" name="media_route_status_available" />
<java-symbol type="string" name="media_route_status_not_available" />
+ <java-symbol type="string" name="owner_name" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -962,6 +963,7 @@
<java-symbol type="drawable" name="status_bar_background" />
<java-symbol type="drawable" name="sym_keyboard_shift" />
<java-symbol type="drawable" name="sym_keyboard_shift_locked" />
+ <java-symbol type="drawable" name="sym_keyboard_return_holo" />
<java-symbol type="drawable" name="tab_bottom_left" />
<java-symbol type="drawable" name="tab_bottom_left_v4" />
<java-symbol type="drawable" name="tab_bottom_right" />
@@ -1086,8 +1088,8 @@
<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="keyguard_widget_region" />
<java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
+ <java-symbol type="layout" name="keyguard_add_widget" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -1177,6 +1179,7 @@
<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" />
@@ -1191,12 +1194,17 @@
<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_sim_puk_account_full_screen" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
<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" />
@@ -1204,6 +1212,10 @@
<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="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" />
@@ -1222,6 +1234,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="security_frame" />
<java-symbol type="id" name="action_mode_bar_stub" />
<java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="backspace" />
@@ -1280,6 +1293,7 @@
<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" />
@@ -1304,12 +1318,15 @@
<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="kg_widget_region" />
- <java-symbol type="id" name="left_strip" />
- <java-symbol type="id" name="right_strip" />
<java-symbol type="id" name="keyguard_multi_user_selector" />
<java-symbol type="id" name="status_security_message" />
-
+ <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="sliding_layout" />
+ <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="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />
@@ -1318,6 +1335,7 @@
<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_screen_glogin_unlock" />
@@ -1334,6 +1352,7 @@
<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" />
@@ -1403,6 +1422,9 @@
<java-symbol type="style" name="Animation.LockScreen" />
<java-symbol type="style" name="Theme.Dialog.RecentApplications" />
<java-symbol type="style" name="Theme.ExpandedMenu" />
+ <java-symbol type="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" />
diff --git a/docs/downloads/training/Animations.zip b/docs/downloads/training/Animations.zip
new file mode 100644
index 0000000..5063dd1
--- /dev/null
+++ b/docs/downloads/training/Animations.zip
Binary files differ
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 14ab5d5..9c21b3a 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -23,8 +23,6 @@
<a class="close" onclick="$('#player-wrapper').hide()">close video</a>
</div>
</div>
-
-
<div class="wrap">
<!-- Slideshow -->
<div class="slideshow-container slideshow-develop col-16">
@@ -32,6 +30,45 @@
<a href="" class="slideshow-next">Next</a>
<div class="frame">
<ul>
+ <li class="item carousel-home">
+ <div class="col-8">
+ <img
+src="http://4.bp.blogspot.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
+class="play no-shadow no-transform" />
+ </div>
+ <div class="content-right col-6">
+ <h2>Building Great Apps for Tablets</h2>
+ <p>Tablets are a growing part of the Android installed base and they offer new opportunities for user engagement and monetization. If you are targeting tablets, check out the <strong>Tablet App Quality Checklist</strong> for tips and techniques on how to deliver a great app experience for tablet users. </p>
+ <p><a
+href="/distribute/googleplay/quality/tablet.html" class="button">Read
+more</a></p>
+ </div>
+ </li>
+ <li class="item carousel-home">
+ <div class="col-8">
+ <img
+src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
+ <div class="content-right col-6">
+ <h2>In-app Subscriptions with Trials</h2>
+ <p>You can now set up a <strong>free trial period</strong> for any Google Play in-app subscription, making it easy for users try your subscriber content before automatically converting to a full subscription. Free trials give you a new way to bring users into your products and engage them effectively. </p>
+ <p><a class="button"
+href="http://dirkbd.mtv:8809/guide/google/play/billing/billing_subscriptions.html#trials">Read
+more</a></p>
+ </div>
+ </li>
+ <li class="item carousel-home">
+ <div class="col-8">
+ <img
+src="http://2.bp.blogspot.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
+ <div class="content-right col-6">
+ <p class="title-intro">From the blog:</p>
+ <h2>New Google Play Developer Console</h2>
+ <p>All developers can now try the <strong>new Google Play Developer Console</strong>. With a streamlined publishing flow, new language options, and new user ratings statistics, you’ll have better tools for delivering great Android apps that delight users.</p>
+ <p><a
+href="http://android-developers.blogspot.com/2012/10/new-google-play-developer-console.html" class="button">Read
+more</a></p>
+ </div>
+ </li>
<li class="item carousel-home">
<div class="col-8">
<img
@@ -42,60 +79,14 @@
<p class="title-intro">From the blog:</p>
<h2>Getting Your App Ready for Jelly Bean and Nexus 7</h2>
<p>For many people, their first taste of Jelly Bean will be on the beautiful
- Nexus 7. While most applications will run just fine on Nexus 7, who wants
- their app to be just fine? Here are some tips for optimizing your application
+ <strong>Nexus 7 tablet</strong>. Most applications will run just fine on Nexus 7, but who wants
+ their app to be just fine? Here are some tips for optimizing your app
to make the most of this device.</p>
<p><a
href="http://android-developers.blogspot.com/2012/07/getting-your-app-ready-for-jelly-bean.html" class="button">Read
more</a></p>
</div>
</li>
- <li class="item carousel-home">
- <div class="col-8">
- <img
-src="http://1.bp.blogspot.com/-6qyjPxTuzv0/T6lde-Oq_fI/AAAAAAAABXc/zle7OFEGP44/s400/fddns%2Bcopy.png"
-class="play no-shadow no-transform" />
- </div>
- <div class="content-right col-6">
- <p class="title-intro">From the blog:</p>
- <h2>Using DialogFragments</h2>
- <p>In this post, I'll show how to use DialogFragments with the <a
-href="http://developer.android.com/reference/android/support/v4/app/DialogFragment.html">v4 support
-library</a> (for backward compatibility on pre-Honeycomb devices) to show a simple edit dialog and
-return a result to the calling Activity using an interface.</p>
- <p><a
-href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html" class="button">Read
-more</a></p>
- </div>
- </li>
- <li class="item carousel-home">
- <div class="col-8">
- <img
-src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
- <div class="content-right col-6">
- <p class="title-intro">From the blog:</p>
- <h2>In-app Subscriptions in Google Play</h2>
- <p>Starting today, developers can use In-app Billing to sell monthly or annual
-subscriptions from inside of their apps. All subscriptions are auto-renewing, for every app and game
-and every type of subscription product.</p>
- <p><a class="button"
-href="http://android-developers.blogspot.com/2012/05/in-app-subscriptions-in-google-play.html">Read
-more</a></p>
- </div>
- </li>
- <li class="item carousel-home">
- <div class="col-8">
- <img
-src="{@docRoot}images/home/developers_live.png" class="play"></div>
- <div class="content-right col-6">
- <h2>Learn what great apps are made of</h2>
- <p>Every week we host a live broadcast in which we review a collection of apps and games
- nominated by the creators. It's no-holds-barred and we tell you exactly what is flawed or
- fantastic in each app and how to make improvements.</p>
- <p><a href="" class="button" onclick="$('ul#DevelopersLive li:first
-a').click();return false;">Watch the latest review</a></p>
- </div>
- </li>
</ul>
</div>
</div>
@@ -111,53 +102,47 @@
<div class="feed-container">
<div class="feed-frame">
<!-- DEVELOPER NEWS -->
- <ul>
- <li><a href="http://android-developers.blogspot.com/2012/06/android-sdk-tools-revision-20.html">
- <div class="feed-image" style="background:url('http://1.bp.blogspot.com/-Kp1qE5du6l8/T-xurIjfPgI/AAAAAAAABAM/kuWQwPgi0rw/s640/newactivity+(1).png') no-repeat 0 0">
- </div>
- <h4>Android SDK Tools, Revision 20</h4>
- <p>Along with the preview of the Android 4.1 (Jelly Bean) platform, we launched Android SDK Tools R20 and ADT 20.0.0. Here are a few things...</p>
- </a></li>
- <li><a href="http://android-developers.blogspot.com/2012/04/faster-emulator-with-better-hardware.html">
- <div class="feed-image" style="background:url('../images/emulator-wvga800l.png') no-repeat 0 0">
- </div>
- <h4>A Faster Emulator with Better...</h4>
- <p>Today we’re thrilled to announce several significant improvements to the emulator, including a dramatic...</p>
- </a></li>
- <li><a href="http://android-developers.blogspot.com/2012/04/android-c2dm-client-login-key.html">
- <div class="feed-image" style="background:url('../images/develop/auth-code.png') no-repeat 0 0">
- </div>
- <h4>Android C2DM — Client Login key...</h4>
- <p>In the upcoming weeks, some of the older Client
- Login authentication keys will expire. If you generated the token you’re...</p>
- </a></li>
- <li><a href="http://android-developers.blogspot.com/2012/04/accessibility-are-you-serving-all-your.html">
- <div class="feed-image">
- </div>
- <h4>Accessibility</h4>
- <p>We recently published some new resources to help developers make their Android applications more accessible... </p>
- </a></li>
-
- </ul>
+ <ul>
+ <li><a href="http://android-developers.blogspot.com/2012/10/google-play-seller-support-in-india.html">
+ <div class="feed-image" style="background:url('http://4.bp.blogspot.com/-ekT-9XQi0YY/UH7WT2XjSdI/AAAAAAAABwc/fI5QaPi7QEk/s320/india-apps1.png') no-repeat 0 0"></div>
+ <h4>Google Play Seller Support in India</h4>
+ <p>Developers in India can sell paid applications, in-app products, and subscriptions in Google Play, with monthly payouts to their local bank accounts...</p>
+ </a></li>
+ <li><a href="http://android-developers.blogspot.com/2012/09/google-play-services-and-oauth-identity.html">
+ <div class="feed-image" style="background:url('https://lh4.ggpht.com/7z4NItEg-X21zvFGAarKonk-VaysBYthJ30u1JjaQ0-5fjyHNawnmoNeG--4FCACog=w124') no-repeat 0 0"></div>
+ <h4>Google Play services and OAuth Tools</h4>
+ <p>The rollout of Google Play services to all Android 2.2+ devices worldwide is now complete, and all of those devices now have new tools for working with OAuth 2.0 tokens...</p>
+ </a></li>
+ <li><a href="http://android-developers.blogspot.com/2012/08/creating-your-own-spelling-checker.html">
+ <div class="feed-image" style="background:url('http://2.bp.blogspot.com/-QKlztEdM1aA/UC1bH6Kv4UI/AAAAAAAAADo/fQS8-EfBYIQ/s320/spell-check-framed-new.png') no-repeat 0 0"></div>
+ <h4>Creating A Spelling Checker Service</h4>
+ <p>If you are an IME developer, the Spelling Checker framework gives you a great way to provide an even better experience for your users...</p>
+ </a></li>
+ <li><a href="http://android-developers.blogspot.com/2012/04/accessibility-are-you-serving-all-your.html">
+ <div class="feed-image"></div>
+ <h4>Accessibility</h4>
+ <p>We recently published some new resources to help developers make their Android applications more accessible... </p>
+ </a></li>
+ </ul>
<!-- FEATURED DOCS -->
- <ul>
- <li><a href="{@docRoot}guide/google/play/billing/index.html">
- <h4>Google Play In-app Billing</h4>
- <p>In-app Billing is a Google Play service that lets you sell digital content from inside your applications. You can sell products as standard in-app products (one-time purchase) or with subscriptions (recurring...</p>
- </a></li>
- <li><a href="{@docRoot}guide/topics/providers/contacts-provider.html">
- <h4>Contacts Provider</h4>
- <p>The Contacts Provider is a powerful and flexible Android component that manages the device's central repository of data about people. You can use it to build powerful social features...</p>
- </a></li>
- <li><a href="{@docRoot}training/efficient-downloads/index.html">
- <h4>Transferring Data Without Draining the Battery</h4>
- <p>This training class demonstrates the best practices for scheduling and executing downloads using techniques such as caching, polling, and prefetching.</p>
- </a></li>
- <li><a href="{@docRoot}training/backward-compatible-ui/index.html">
- <h4>Creating Backward-Compatible UIs</h4>
- <p>This training class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions...</p>
- </a></li>
- </ul>
+ <ul>
+ <li><a href="{@docRoot}distribute/googleplay/spotlight/tablets.html">
+ <h4>Tablet Stories</h4>
+ <p>More developers are investing in a full tablet experience for their apps. Here are some stories from developers who are seeing real results as they expand their offering to include Android tablets. </p>
+ </a></li>
+ <li><a href="{@docRoot}distribute/googleplay/quality/core.html">
+ <h4>Core App Quality Guidelines</h4>
+ <p>This document helps you assess basic aspects of quality in your app through a compact set of core app quality criteria and associated tests. All Android apps should meet these criteria.</p>
+ </a></li>
+ <li><a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">
+ <h4>Updated Notifications API Guide</h4>
+ <p>The Notifications API Guide is updated to include information about building Jelly Bean rich notifications using the Support Library APIs for backwards-compatibility.</p>
+ </a></li>
+ <li><a href="{@docRoot}guide/topics/ui/dialogs.html">
+ <h4>Updated Dialogs API Guide</h4>
+ <p>The Dialogs API Guide now shows to use DialogFragment class, a simpler way to manage your dialogs and embed them in alternative layouts.</p>
+ </a></li>
+ </ul>
</div>
</div>
</div> <!-- /news and feature feed -->
@@ -169,10 +154,10 @@
</ul>
<div class="feed-container">
<div class="feed-frame">
- <ul id="DevelopersLive">
- </ul>
- <ul id="VideoPlaylists">
- </ul>
+ <ul id="DevelopersLive">
+ </ul>
+ <ul id="VideoPlaylists">
+ </ul>
</div>
</div>
</div>
diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4
new file mode 100755
index 0000000..e885f98
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv
new file mode 100755
index 0000000..33cd86c
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm
new file mode 100755
index 0000000..a670d78
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.mp4 b/docs/html/training/animation/anim_crossfade.mp4
new file mode 100644
index 0000000..ced7cc9
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.ogv b/docs/html/training/animation/anim_crossfade.ogv
new file mode 100644
index 0000000..7ec417a
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.webm b/docs/html/training/animation/anim_crossfade.webm
new file mode 100644
index 0000000..21e7228
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.mp4 b/docs/html/training/animation/anim_layout_changes.mp4
new file mode 100644
index 0000000..0709601
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.ogv b/docs/html/training/animation/anim_layout_changes.ogv
new file mode 100644
index 0000000..75f5250
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.webm b/docs/html/training/animation/anim_layout_changes.webm
new file mode 100644
index 0000000..a99a566
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4
new file mode 100755
index 0000000..3e65026
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv
new file mode 100755
index 0000000..c45ebd4
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm
new file mode 100755
index 0000000..c72adbc
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.mp4 b/docs/html/training/animation/anim_zoom.mp4
new file mode 100644
index 0000000..4326c35
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.ogv b/docs/html/training/animation/anim_zoom.ogv
new file mode 100644
index 0000000..e5793f3
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.webm b/docs/html/training/animation/anim_zoom.webm
new file mode 100644
index 0000000..b3b7566
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.webm
Binary files differ
diff --git a/docs/html/training/animation/cardflip.jd b/docs/html/training/animation/cardflip.jd
new file mode 100644
index 0000000..ab3eb3a
--- /dev/null
+++ b/docs/html/training/animation/cardflip.jd
@@ -0,0 +1,365 @@
+page.title=Displaying Card Flip Animations
+trainingnavtop=true
+
+@jd:body
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to
+ </h2>
+ <ol>
+ <li>
+ <a href="#animators">Create the Animators</a>
+ </li>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#fragment">Create the Fragment</a>
+ </li>
+ <li>
+ <a href="#animate">Animate the Card Flip</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+ <p> This lesson shows you how to do a card flip
+ animation with custom fragment animations.
+ Card flips animate between views of content by showing an animation that emulates
+ a card flipping over.
+ </p>
+ <p>Here's what a card flip looks like:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_card_flip.mp4" type="video/mp4">
+ <source src="anim_card_flip.webm" type="video/webm">
+ <source src="anim_card_flip.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Card flip animation
+ <div class="video-instructions"> </div>
+ </div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+ run the sample app and select the Card Flip example. See the following
+ files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/CardFlipActivity.java</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_in.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_out.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_in.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_left_out.xml</code>
+ </li>
+ <li>
+ <code>layout/fragment_card_back.xml</code>
+ </li>
+ <li>
+ <code>layout/fragment_card_front.xml</code>
+ </li>
+ </ul>
+
+ <h2 id="animate">
+ Create the Animators
+ </h2>
+ <p>
+ Create the animations for the card flips. You'll need two animators for when the front
+ of the card animates out and to the left and in and from the left. You'll also need two animators
+ for when the back of the card animates in and from the right and out and to the right.
+ </p>
+ <h4>
+ card_flip_left_in.xml
+ </h4>
+<pre>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Before rotating, immediately set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" />
+
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="-180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
+ <objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set>
+</pre>
+ <h4>
+ card_flip_left_out.xml
+ </h4>
+ <pre>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set>
+ </pre>
+ <h4>
+ card_flip_right_in.xml
+ </h4>
+ <pre>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Before rotating, immediately set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" />
+
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
+ <objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set>
+
+</pre>
+ <h4>
+ card_flip_right_out.xml
+ </h4>
+ <pre>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="-180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set>
+</pre>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Each side of the "card" is a separate layout that can contain any content you want,
+ such as two screens of text, two images, or any combination of views to flip between. You'll then
+ use the two layouts in the fragments that you'll later animate. The following layouts
+ create one side of a card that shows text:
+ </p>
+
+ <pre>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="#a6c"
+ android:padding="16dp"
+ android:gravity="bottom">
+
+ <TextView android:id="@android:id/text1"
+ style="?android:textAppearanceLarge"
+ android:textStyle="bold"
+ android:textColor="#fff"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/card_back_title" />
+
+ <TextView style="?android:textAppearanceSmall"
+ android:textAllCaps="true"
+ android:textColor="#80ffffff"
+ android:textStyle="bold"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/card_back_description" />
+
+</LinearLayout>
+</pre>
+<p>
+and the other side of the card that displays an {@link android.widget.ImageView}:
+</p>
+<pre>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/image1"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_image_1" />
+</pre>
+ <h2 id="fragment">
+ Create the Fragment
+ </h2>
+ <p>
+ Create fragment classes for the front and back of the card. These classes return the layouts
+ that you created previously in the {@link android.app.Fragment#onCreateView onCreateView()} method
+ of each fragment. You can then create instances of this fragment in the parent activity
+ where you want to show the card. The following example shows nested fragment classes inside
+ of the parent activity that uses them:
+ </p>
+ <pre>
+public class CardFlipActivity extends Activity {
+ ...
+ /**
+ * A fragment representing the front of the card.
+ */
+ public class CardFrontFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_card_front, container, false);
+ }
+ }
+
+ /**
+ * A fragment representing the back of the card.
+ */
+ public class CardBackFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_card_back, container, false);
+ }
+ }
+}
+</pre>
+ <h2 id="animate">
+ Animate the Card Flip
+ </h2>
+
+ <p> Now, you'll need to display the fragments inside of a parent activity.
+ To do this, first create the layout for your activity. The following example creates a
+ {@link android.widget.FrameLayout} that you
+ can add fragments to at runtime:</p>
+
+ <pre>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</pre>
+
+ <p>In the activity code, set the content view to be the layout that you just created. It's also
+ good idea to show a default fragment when the activity is created, so the following example
+ activity shows you how to display the front of the card by default:
+ </p>
+
+
+
+<pre>
+public class CardFlipActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_activity_card_flip);
+
+ if (savedInstanceState == null) {
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.container, new CardFrontFragment())
+ .commit();
+ }
+ }
+ ...
+}
+</pre>
+ <p>
+ Now that you have the front of the card showing, you can show the back of the card
+ with the flip animation at an appropriate time. Create a method to show the other
+ side of the card that does the following things:
+ </p>
+ <ul>
+ <li>Sets the custom animations that you created earlier for the fragment transitions.
+ </li>
+ <li>Replaces the currently displayed fragment with a new fragment and animates this event
+ with the custom animations that you created.
+ </li>
+ <li>Adds the previously displayed fragment to the fragment back stack
+ so when the user presses the <em>Back</em> button, the card flips back over.
+ </li>
+ </ul>
+ <pre>
+private void flipCard() {
+ if (mShowingBack) {
+ getFragmentManager().popBackStack();
+ return;
+ }
+
+ // Flip to the back.
+
+ mShowingBack = true;
+
+ // Create and commit a new fragment transaction that adds the fragment for the back of
+ // the card, uses custom animations, and is part of the fragment manager's back stack.
+
+ getFragmentManager()
+ .beginTransaction()
+
+ // Replace the default fragment animations with animator resources representing
+ // rotations when switching to the back of the card, as well as animator
+ // resources representing rotations when flipping back to the front (e.g. when
+ // the system Back button is pressed).
+ .setCustomAnimations(
+ R.animator.card_flip_right_in, R.animator.card_flip_right_out,
+ R.animator.card_flip_left_in, R.animator.card_flip_left_out)
+
+ // Replace any fragments currently in the container view with a fragment
+ // representing the next page (indicated by the just-incremented currentPage
+ // variable).
+ .replace(R.id.container, new CardBackFragment())
+
+ // Add this transaction to the back stack, allowing users to press Back
+ // to get to the front of the card.
+ .addToBackStack(null)
+
+ // Commit the transaction.
+ .commit();
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/crossfade.jd b/docs/html/training/animation/crossfade.jd
new file mode 100644
index 0000000..99e879b
--- /dev/null
+++ b/docs/html/training/animation/crossfade.jd
@@ -0,0 +1,208 @@
+page.title=Crossfading Two Views
+trainingnavtop=true
+
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to:
+ </h2>
+ <ol>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#setup">Set up the Animation</a>
+ </li>
+ <li>
+ <a href="#animate">Crossfade the Views</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+
+ <p>
+ Crossfade animations (also know as dissolve) gradually fade out one UI component while simultaneously fading in
+ another. This animation is useful for situations where you want to switch content or views
+ in your app. Crossfades are very subtle and short but offer a fluid transition from one screen to the
+ next. When you don't use them, however, transitions often feel abrupt or hurried.
+ </p>
+ <p>Here's an example of a crossfade from a progress indicator to some text content.
+ </p>
+
+<div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_crossfade.mp4" type="video/mp4">
+ <source src="anim_crossfade.webm" type="video/webm">
+ <source src="anim_crossfade.ogv" type="video/ogg">
+ </video>
+</div>
+<div class="figure-caption">
+Crossfade animation
+ <div class="video-instructions"> </div>
+</div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a>
+ and run the sample app and select the Crossfade example.
+ See the following files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/CrossfadeActivity.java</code>
+ </li>
+ <li>
+ <code>layout/activity_crossfade.xml</code>
+ </li>
+ <li>
+ <code>menu/activity_crossfade.xml</code>
+ </li>
+ </ul>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Create the two views that you want to crossfade. The following example creates a progress
+ indicator and a scrollable text view:
+ </p>
+ <pre>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView style="?android:textAppearanceMedium"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lorem_ipsum"
+ android:padding="16dp" />
+
+ </ScrollView>
+
+ <ProgressBar android:id="@+id/loading_spinner"
+ style="?android:progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+
+</FrameLayout>
+</pre>
+ <h2 id="setup">
+ Set up the Animation
+ </h2>
+ <p>
+ To set up the animation:
+ </p>
+ <ol>
+ <li>Create member variables for the views that you want to crossfade. You need
+ these references later when modifying the views during the animation.
+ </li>
+ <li>For the view that is being faded in, set its visibility to {@link
+ android.view.View#GONE}. This prevents the view from taking up layout space and omits it
+ from layout calculations, speeding up processing.
+ </li>
+ <li>Cache the <code>{@link android.R.integer#config_shortAnimTime}</code>
+ system property in a member variable. This property defines a standard
+ "short" duration for the animation. This duration is ideal for subtle animations or
+ animations that occur very frequently. {@link android.R.integer#config_longAnimTime} and
+ {@link android.R.integer#config_mediumAnimTime} are also available if you wish to use them.
+ </li>
+ </ol>
+ <p>
+ Here's an example using the layout from the previous code snippet as the activity content
+ view:
+ </p>
+ <pre>
+public class CrossfadeActivity extends Activity {
+
+ private View mContentView;
+ private View mLoadingView;
+ private int mShortAnimationDuration;
+
+ ...
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crossfade);
+
+ mContentView = findViewById(R.id.content);
+ mLoadingView = findViewById(R.id.loading_spinner);
+
+ // Initially hide the content view.
+ mContentView.setVisibility(View.GONE);
+
+ // Retrieve and cache the system's default "short" animation time.
+ mShortAnimationDuration = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ }
+
+</pre>
+ <h2 id="animate">
+ Crossfade the Views
+ </h2>
+ <p>
+ Now that the views are properly set up, crossfade them by doing the following:
+ </p>
+ <ol>
+ <li>For the view that is fading in, set the alpha value to <code>0</code> and the visibility
+ to {@link android.view.View#VISIBLE}. (Remember that it was initially set to {@link
+ android.view.View#GONE}.) This makes the view visible but completely transparent.
+ </li>
+ <li>For the view that is fading in, animate its alpha value from <code>0</code> to
+ <code>1</code>. At the same time, for the view that is fading out, animate the alpha value
+ from <code>1</code> to <code>0</code>.
+ </li>
+ <li>Using {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
+ in an {@link android.animation.Animator.AnimatorListener}, set the visibility of the view
+ that was fading out to {@link android.view.View#GONE}. Even though the alpha value is <code>0</code>,
+ setting the view's visibility to {@link android.view.View#GONE} prevents the view from taking
+ up layout space and omits it from layout calculations, speeding up processing.
+ </li>
+ </ol>
+ <p>
+ The following method shows an example of how to do this:
+ </p>
+ <pre>
+private View mContentView;
+private View mLoadingView;
+private int mShortAnimationDuration;
+
+...
+
+private void crossfade() {
+
+ // Set the content view to 0% opacity but visible, so that it is visible
+ // (but fully transparent) during the animation.
+ mContentView.setAlpha(0f);
+ mContentView.setVisibility(View.VISIBLE);
+
+ // Animate the content view to 100% opacity, and clear any animation
+ // listener set on the view.
+ mContentView.animate()
+ .alpha(1f)
+ .setDuration(mShortAnimationDuration)
+ .setListener(null);
+
+ // Animate the loading view to 0% opacity. After the animation ends,
+ // set its visibility to GONE as an optimization step (it won't
+ // participate in layout passes, etc.)
+ mHideView.animate()
+ .alpha(0f)
+ .setDuration(mShortAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHideView.setVisibility(View.GONE);
+ }
+ });
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/index.jd b/docs/html/training/animation/index.jd
new file mode 100644
index 0000000..9cc7e6c
--- /dev/null
+++ b/docs/html/training/animation/index.jd
@@ -0,0 +1,86 @@
+page.title=Adding Animations
+trainingnavtop=true
+startpage=true
+
+@jd:body
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ Dependencies and prerequisites
+ </h2>
+ <ul>
+ <li>Android 4.0 or later
+ </li>
+ <li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
+ Interface</a>
+ </li>
+ </ul>
+ <h2>
+ You should also read
+ </h2>
+ <ul>
+ <li>
+ <a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>
+ </li>
+ </ul>
+ <h2>
+ Try it out
+ </h2>
+ <div class="download-box">
+ <a href="{@docRoot}shareables/training/Animations.zip" class=
+ "button">Download the sample app</a>
+ <p class="filename">
+ Animations.zip
+ </p>
+ </div>
+ </div>
+ </div>
+ <p>
+ Animations can add subtle visual cues that notify users about what's going on in your app and
+ improve their mental model of your app's interface. Animations are especially useful when the
+ screen changes state, such as when content loads or new actions become available. Animations
+ can also add a polished look to your app, which gives your app a higher quality feel.
+ </p>
+ <p>
+ Keep in mind though, that overusing animations or using them at the wrong time can be
+ detrimental, such as when they cause delays. This training class shows you how to
+ implement some common types of animations that can increase usability and add flair without
+ annoying your users.
+ </p>
+
+ <h2>
+ Lessons
+ </h2>
+ <dl>
+ <dt>
+ <b><a href="crossfade.html">Crossfading Two Views</a></b>
+ </dt>
+ <dd>
+ Learn how to crossfade between two overlapping views. This lesson shows you how to crossfade a progress
+ indicator to a view that contains text content.
+ </dd>
+ <dt>
+ <b><a href="screen-slide.html">Using ViewPager for Screen Slides</a></b>
+ </dt>
+ <dd>
+ Learn how to animate between horizontally adjacent screens with a sliding transition.
+ </dd>
+ <dt>
+ <b><a href="cardflip.html">Displaying Card Flip Animations</a></b>
+ </dt>
+ <dd>
+ Learn how to animate between two views with a flipping motion.
+ </dd>
+ <dt>
+ <b><a href="zoom.html">Zooming a View</a></b>
+ </dt>
+ <dd>
+ Learn how to enlarge views with a touch-to-zoom animation.
+ </dd>
+ <dt>
+ <b><a href="layout.html">Animating Layout Changes</a></b>
+ </dt>
+ <dd>
+ Learn how to enable built-in animations when adding, removing, or updating child views in a layout.
+ </dd>
+ </dl>
\ No newline at end of file
diff --git a/docs/html/training/animation/layout.jd b/docs/html/training/animation/layout.jd
new file mode 100644
index 0000000..b8e0077
--- /dev/null
+++ b/docs/html/training/animation/layout.jd
@@ -0,0 +1,77 @@
+page.title=Animating Layout Changes
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+ <ol>
+ <li><a href="#views">Create the Layout</a></li>
+ <li><a href="#add">Add, Update, or Remove Items from the Layout</a></li>
+ </ol>
+
+</div>
+</div>
+
+ <p>A layout animation is a pre-loaded animation that the system runs each time you make a change
+ to the layout configuration. All you need to do is set an attribute in the layout to tell the
+ Android system to animate these layout changes, and system-default animations are carried out for you.
+ </p>
+<p class="note"><strong>Tip</strong>: If you want to supply custom layout animations,
+create a {@link android.animation.LayoutTransition} object and supply it to
+the layout with the {@link android.view.ViewGroup#setLayoutTransition setLayoutTransition()}
+method.
+</p>
+ Here's what a default layout animation looks like when adding items to a list:
+</p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_layout_changes.mp4" type="video/mp4">
+ <source src="anim_layout_changes.webm" type="video/webm">
+ <source src="anim_layout_changes.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Layout animation
+ <div class="video-instructions"> </div>
+ </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+run the sample app and select the Crossfade example. See the following files for the
+code implementation:</p>
+<ol>
+ <li><code>src/LayoutChangesActivity.java</code></li>
+ <li><code>layout/activity_layout_changes.xml</code></li>
+ <li><code>menu/activity_layout_changes.xml</code></li>
+</ol>
+
+<h2 id="views">Create the Layout</h2>
+<p>In your activity's layout XML file, set the <code>android:animateLayoutChanges</code>
+ attribute to <code>true</code> for the layout that you want to enable animations for.
+ For instance:</p>
+
+<pre>
+<LinearLayout android:id="@+id/container"
+ android:animateLayoutChanges="true"
+ ...
+/>
+</pre>
+
+<h2 id="activity">Add, Update, or Remove Items from the Layout</h2>
+<p>
+Now, all you need to do is add, remove, or update items in the layout
+and the items are animated automatically:
+</p>
+<pre>
+private ViewGroup mContainerView;
+...
+private void addItem() {
+ View newView;
+ ...
+ mContainerView.addView(newView, 0);
+}
+</pre>
diff --git a/docs/html/training/animation/screen-slide.jd b/docs/html/training/animation/screen-slide.jd
new file mode 100755
index 0000000..8a7af67
--- /dev/null
+++ b/docs/html/training/animation/screen-slide.jd
@@ -0,0 +1,185 @@
+page.title=Using ViewPager for Screen Slides
+trainingnavtop=true
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#views">Create the Views</a></li>
+ <li><a href="#fragment">Create the Fragment</a></li>
+ <li><a href="#viewpager">Animate the Screen Slide</a></li>
+ </ol>
+ </div>
+ </div>
+ <p>
+ Screen slides are transitions between one entire screen to another and are common with UIs
+ like setup wizards or slideshows. This lesson shows you how to do screen slides with
+ a {@link android.support.v4.view.ViewPager} provided by the <a href=
+ "{@docRoot}/tools/extras/support-library.html">support library</a>.
+ {@link android.support.v4.view.ViewPager}s can animate screen slides
+ automatically. Here's what a screen slide looks like that transitions from
+ one screen of content to the next:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_screenslide.mp4" type="video/mp4">
+ <source src="anim_screenslide.webm" type="video/webm">
+ <source src="anim_screenslide.ogv" type="video/ogg">
+ </video>
+ </div>
+
+ <div class="figure-caption">
+ Screen slide animation
+ <div class="video-instructions"> </div>
+ </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a>
+and run the sample app and select the Screen Slide example. See the
+following files for the code implementation:</p>
+<ul>
+ <li><code>src/ScreenSlidePageFragment.java</code></li>
+ <li><code>src/ScreenSlideActivity.java</code></li>
+ <li><code>layout/activity_screen_slide.xml</code></li>
+ <li><code>layout/fragment_screen_slide_page.xml</code></li>
+</ul>
+
+<h2 id="views">Create the Views</h2>
+ <p>Create a layout file that you'll later use for the content of a fragment. The following example
+ contains a text view to display some text:
+
+<pre>
+<com.example.android.animationsdemo.ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView style="?android:textAppearanceMedium"
+ android:padding="16dp"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lorem_ipsum" />
+
+</com.example.android.animationsdemo.ScrollView>
+</pre>
+
+<h2 id="fragment">Create the Fragment</h2>
+<p>Create a {@link android.support.v4.app.Fragment} class that returns the layout
+that you just created in the {@link android.app.Fragment#onCreateView onCreateView()}
+ method. You can then create instances of this fragment in the parent activity whenever you need a new page to
+ display to the user:</p>
+
+
+<pre>
+public class ScreenSlidePageFragment extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup rootView = (ViewGroup) inflater.inflate(
+ R.layout.fragment_screen_slide_page, container, false);
+
+ return rootView;
+ }
+}
+</pre>
+
+<h2 id="viewpager">Screen Slides with ViewPager</h2>
+
+<p>{@link android.support.v4.view.ViewPager}s have built-in swipe gestures to transition
+ through pages, and they display screen slide animations by default, so you don't need to create any. {@link android.support.v4.view.ViewPager}s use
+{@link android.support.v4.view.PagerAdapter}s as a supply for new pages to display, so the {@link android.support.v4.view.PagerAdapter} will use the
+fragment class that you created earlier.
+ </p>
+
+<p>To begin, create a layout that contains a {@link android.support.v4.view.ViewPager}:</p>
+
+<pre>
+<android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</pre>
+
+<p>Create an activity that does the following things:
+</p>
+
+<ul>
+ <li>Sets the content view to be the layout with the {@link android.support.v4.view.ViewPager}.</li>
+ <li>Create a class that extends the {@link android.support.v13.app.FragmentStatePagerAdapter} abstract class. Implement
+ the {@link android.support.v4.app.FragmentStatePagerAdapter#getItem getItem()} method to supply
+ instances of <code>ScreenSlidePageFragment</code> as new pages. The pager adapter also requires that you implement the
+ {@link android.support.v4.view.PagerAdapter#getCount getCount()} method, which returns the amount of pages the adapter will create (five in the example).
+ <li>Hooks up the {@link android.support.v4.view.PagerAdapter} to the {@link android.support.v4.view.ViewPager}</code>.</li>
+ <li>Handle's the device's back button by moving backwards in the virtual stack of fragments.
+ If the user is already on the first page, go back on the activity back stack.</li>
+</ul>
+
+<pre>
+public class ScreenSlidePagerActivity extends FragmentActivity {
+ /**
+ * The number of pages (wizard steps) to show in this demo.
+ */
+ private static final int NUM_PAGES = 5;
+
+ /**
+ * The pager widget, which handles animation and allows swiping horizontally to access previous
+ * and next wizard steps.
+ */
+ private ViewPager mPager;
+
+ /**
+ * The pager adapter, which provides the pages to the view pager widget.
+ */
+ private PagerAdapter mPagerAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_screen_slide_pager);
+
+ // Instantiate a ViewPager and a PagerAdapter.
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
+ mPager.setAdapter(mPagerAdapter);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mPager.getCurrentItem() == 0) {
+ // If the user is currently looking at the first step, allow the system to handle the
+ // Back button. This calls finish() on this activity and pops the back stack.
+ super.onBackPressed();
+ } else {
+ // Otherwise, select the previous step.
+ mPager.setCurrentItem(mPager.getCurrentItem() - 1);
+ }
+ }
+
+ /**
+ * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
+ * sequence.
+ */
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+ public ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return new ScreenSlidePageFragment();
+ }
+
+ @Override
+ public int getCount() {
+ return NUM_PAGES;
+ }
+ }
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/zoom.jd b/docs/html/training/animation/zoom.jd
new file mode 100644
index 0000000..5dc2b6c
--- /dev/null
+++ b/docs/html/training/animation/zoom.jd
@@ -0,0 +1,320 @@
+page.title=Zooming a View
+trainingnavtop=true
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to:
+ </h2>
+ <ol>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#setup">Set up the Zoom Animation</a>
+ </li>
+ <li>
+ <a href="#animate">Zoom the View</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+ <p>
+ This lesson demonstrates how to do a touch-to-zoom animation, which is useful for apps such as photo
+ galleries to animate a view from a thumbnail to a full-size image that fills the screen.
+ </p>
+ <p>Here's what a touch-to-zoom animation looks like that
+ expands an image thumbnail to fill the screen:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_zoom.mp4" type="video/mp4">
+ <source src="anim_zoom.webm" type="video/webm">
+ <source src="anim_zoom.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Zoom animation
+ <div class="video-instructions"> </div>
+ </div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+ run the sample app and select the
+ Zoom example. See the following files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/TouchHighlightImageButton.java</code> (a simple helper class that shows a blue
+ touch highlight when the image button is pressed)
+ </li>
+ <li>
+ <code>src/ZoomActivity.java</code>
+ </li>
+ <li>
+ <code>layout/activity_zoom.xml</code>
+ </li>
+ </ul>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Create a layout file that contains the small and large version of the content that you want
+ to zoom. The following example creates an {@link android.widget.ImageButton} for clickable image thumbnail
+ and an {@link android.widget.ImageView} that displays the enlarged view of the image:
+ </p>
+ <pre>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <ImageButton
+ android:id="@+id/thumb_button_1"
+ android:layout_width="100dp"
+ android:layout_height="75dp"
+ android:layout_marginRight="1dp"
+ android:src="@drawable/thumb1"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_image_1" />
+
+ </LinearLayout>
+
+ <!-- This initially-hidden ImageView will hold the expanded/zoomed version of
+ the images above. Without transformations applied, it takes up the entire
+ screen. To achieve the "zoom" animation, this view's bounds are animated
+ from the bounds of the thumbnail button above, to its final laid-out
+ bounds.
+ -->
+
+ <ImageView
+ android:id="@+id/expanded_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:contentDescription="@string/description_zoom_touch_close" />
+
+</FrameLayout>
+</pre>
+ <h2 id="setup">
+ Set up the Zoom Animation
+ </h2>
+ <p>
+ Once you apply your layout, set up the event handlers that trigger the zoom animation.
+ The following example adds a {@link android.view.View.OnClickListener} to the {@link
+ android.widget.ImageButton} to execute the zoom animation when the user
+ clicks the image button:
+ </p>
+ <pre>
+public class ZoomActivity extends FragmentActivity {
+ // Hold a reference to the current animator,
+ // so that it can be canceled mid-way.
+ private Animator mCurrentAnimator;
+
+ // The system "short" animation time duration, in milliseconds. This
+ // duration is ideal for subtle animations or animations that occur
+ // very frequently.
+ private int mShortAnimationDuration;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_zoom);
+
+ // Hook up clicks on the thumbnail views.
+
+ final View thumb1View = findViewById(R.id.thumb_button_1);
+ thumb1View.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ zoomImageFromThumb(thumb1View, R.drawable.image1);
+ }
+ });
+
+ // Retrieve and cache the system's default "short" animation time.
+ mShortAnimationDuration = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ }
+ ...
+}
+</pre>
+ <h2 id="animate">
+ Zoom the View
+ </h2>
+ <p>
+ You'll now need to animate from the normal sized view to the zoomed view
+ when appropriate. In general, you need to animate from the bounds of the normal-sized view to the
+ bounds of the larger-sized view. The following method shows you how to implement a zoom animation that
+ zooms from an image thumbnail to an enlarged view by doing the following things:
+ </p>
+ <ol>
+ <li>Assign the high-res image to the hidden "zoomed-in" (enlarged) {@link
+ android.widget.ImageView}. The following example loads a large image resource on the UI
+ thread for simplicity. You will want to do this loading in a separate thread to prevent
+ blocking on the UI thread and then set the bitmap on the UI thread. Ideally, the bitmap
+ should not be larger than the screen size.
+ </li>
+ <li>Calculate the starting and ending bounds for the {@link android.widget.ImageView}.
+ </li>
+ <li>Animate each of the four positioning and sizing properties <code>{@link
+ android.view.View#X}</code>, <code>{@link android.view.View#Y}</code>, ({@link
+ android.view.View#SCALE_X}, and <code>{@link android.view.View#SCALE_Y}</code>)
+ simultaneously, from the starting bounds to the ending bounds. These four animations are
+ added to an {@link android.animation.AnimatorSet} so that they can be started at the same
+ time.
+ </li>
+ <li>Zoom back out by running a similar animation but in reverse when the user touches the
+ screen when the image is zoomed in. You can do this by adding a {@link
+ android.view.View.OnClickListener} to the {@link android.widget.ImageView}. When clicked, the
+ {@link android.widget.ImageView} minimizes back down to the size of the image thumbnail and
+ sets its visibility to {@link android.view.View#GONE} to hide it.
+ </li>
+ </ol>
+ <pre>
+private void zoomImageFromThumb(final View thumbView, int imageResId) {
+ // If there's an animation in progress, cancel it
+ // immediately and proceed with this one.
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.cancel();
+ }
+
+ // Load the high-resolution "zoomed-in" image.
+ final ImageView expandedImageView = (ImageView) findViewById(
+ R.id.expanded_image);
+ expandedImageView.setImageResource(imageResId);
+
+ // Calculate the starting and ending bounds for the zoomed-in image.
+ // This step involves lots of math. Yay, math.
+ final Rect startBounds = new Rect();
+ final Rect finalBounds = new Rect();
+ final Point globalOffset = new Point();
+
+ // The start bounds are the global visible rectangle of the thumbnail,
+ // and the final bounds are the global visible rectangle of the container
+ // view. Also set the container view's offset as the origin for the
+ // bounds, since that's the origin for the positioning animation
+ // properties (X, Y).
+ thumbView.getGlobalVisibleRect(startBounds);
+ findViewById(R.id.container)
+ .getGlobalVisibleRect(finalBounds, globalOffset);
+ startBounds.offset(-globalOffset.x, -globalOffset.y);
+ finalBounds.offset(-globalOffset.x, -globalOffset.y);
+
+ // Adjust the start bounds to be the same aspect ratio as the final
+ // bounds using the "center crop" technique. This prevents undesirable
+ // stretching during the animation. Also calculate the start scaling
+ // factor (the end scaling factor is always 1.0).
+ float startScale;
+ if ((float) finalBounds.width() / finalBounds.height()
+ > (float) startBounds.width() / startBounds.height()) {
+ // Extend start bounds horizontally
+ startScale = (float) startBounds.height() / finalBounds.height();
+ float startWidth = startScale * finalBounds.width();
+ float deltaWidth = (startWidth - startBounds.width()) / 2;
+ startBounds.left -= deltaWidth;
+ startBounds.right += deltaWidth;
+ } else {
+ // Extend start bounds vertically
+ startScale = (float) startBounds.width() / finalBounds.width();
+ float startHeight = startScale * finalBounds.height();
+ float deltaHeight = (startHeight - startBounds.height()) / 2;
+ startBounds.top -= deltaHeight;
+ startBounds.bottom += deltaHeight;
+ }
+
+ // Hide the thumbnail and show the zoomed-in view. When the animation
+ // begins, it will position the zoomed-in view in the place of the
+ // thumbnail.
+ thumbView.setAlpha(0f);
+ expandedImageView.setVisibility(View.VISIBLE);
+
+ // Set the pivot point for SCALE_X and SCALE_Y transformations
+ // to the top-left corner of the zoomed-in view (the default
+ // is the center of the view).
+ expandedImageView.setPivotX(0f);
+ expandedImageView.setPivotY(0f);
+
+ // Construct and run the parallel animation of the four translation and
+ // scale properties (X, Y, SCALE_X, and SCALE_Y).
+ AnimatorSet set = new AnimatorSet();
+ set
+ .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
+ startBounds.left, finalBounds.left))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
+ startBounds.top, finalBounds.top))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
+ startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
+ View.SCALE_Y, startScale, 1f));
+ set.setDuration(mShortAnimationDuration);
+ set.setInterpolator(new DecelerateInterpolator());
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentAnimator = null;
+ }
+ });
+ set.start();
+ mCurrentAnimator = set;
+
+ // Upon clicking the zoomed-in image, it should zoom back down
+ // to the original bounds and show the thumbnail instead of
+ // the expanded image.
+ final float startScaleFinal = startScale;
+ expandedImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.cancel();
+ }
+
+ // Animate the four positioning/sizing properties in parallel,
+ // back to their original values.
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator
+ .ofFloat(expandedImageView, View.X, startBounds.left))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.Y,startBounds.top))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.SCALE_X, startScaleFinal))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.SCALE_Y, startScaleFinal));
+ set.setDuration(mShortAnimationDuration);
+ set.setInterpolator(new DecelerateInterpolator());
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ thumbView.setAlpha(1f);
+ expandedImageView.setVisibility(View.GONE);
+ mCurrentAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ thumbView.setAlpha(1f);
+ expandedImageView.setVisibility(View.GONE);
+ mCurrentAnimator = null;
+ }
+ });
+ set.start();
+ mCurrentAnimator = set;
+ }
+ });
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 4ad1353..4a5b0fa 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -287,6 +287,34 @@
</li>
</ul>
</li>
+
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html">
+ <span class="en">Adding Animations</span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/animation/crossfade.html">
+ <span class="en">Crossfading Two Views</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/screen-slide.html">
+ <span class="en">Using ViewPager for Screen Slide</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/cardflip.html">
+ <span class="en">Displaying Card Flip Animations</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/zoom.html">
+ <span class="en">Zooming a View</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/layout.html">
+ <span class="en">Animating Layout Changes</span>
+ </a>
+ </li>
+ </ul>
+ </li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 03cca84..b3e4a88 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -22,9 +22,7 @@
/**
* Represents a geographical boundary, also known as a geofence.
*
- * <p>Currently only circular geofences are supported, but this object
- * is opaque so could be used in the future to represent polygons or other
- * shapes.
+ * <p>Currently only circular geofences are supported.
*/
public final class Geofence implements Parcelable {
/** @hide */
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 82ed432..2a5a16e 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -32,7 +32,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
-import android.view.DisplayInfo;
import java.util.ArrayList;
import java.util.HashMap;
@@ -53,7 +52,7 @@
public class MediaRouter {
private static final String TAG = "MediaRouter";
- static class Static {
+ static class Static implements DisplayManager.DisplayListener {
final Resources mResources;
final IAudioService mAudioService;
final DisplayManager mDisplayService;
@@ -105,6 +104,8 @@
mDefaultAudioVideo = new RouteInfo(mSystemCategory);
mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
+ mDefaultAudioVideo, getAllPresentationDisplays());
addRouteStatic(mDefaultAudioVideo);
// This will select the active wifi display route if there is one.
@@ -115,6 +116,8 @@
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
+ mDisplayService.registerDisplayListener(this, mHandler);
+
AudioRoutesInfo newAudioRoutes = null;
try {
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -191,6 +194,39 @@
}
}
}
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ public Display[] getAllPresentationDisplays() {
+ return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ }
+
+ private void updatePresentationDisplays(int changedDisplayId) {
+ final Display[] displays = getAllPresentationDisplays();
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ final RouteInfo info = mRoutes.get(i);
+ Display display = choosePresentationDisplayForRoute(info, displays);
+ if (display != info.mPresentationDisplay
+ || (display != null && display.getDisplayId() == changedDisplayId)) {
+ info.mPresentationDisplay = display;
+ dispatchRoutePresentationDisplayChanged(info);
+ }
+ }
+ }
}
static Static sStatic;
@@ -218,6 +254,9 @@
* While remote routing is active the application may use a
* {@link android.app.Presentation Presentation} to replace the mirrored view
* on the external display with different content.</p>
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ * @see android.app.Presentation
*/
public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
@@ -239,6 +278,9 @@
if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
result.append("ROUTE_TYPE_LIVE_AUDIO ");
}
+ if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ result.append("ROUTE_TYPE_LIVE_VIDEO ");
+ }
if ((types & ROUTE_TYPE_USER) != 0) {
result.append("ROUTE_TYPE_USER ");
}
@@ -671,6 +713,14 @@
}
}
+ static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
+ for (CallbackInfo cbi : sStatic.mCallbacks) {
+ if ((cbi.type & info.mSupportedTypes) != 0) {
+ cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
+ }
+ }
+ }
+
static void systemVolumeChanged(int newValue) {
final RouteInfo selectedRoute = sStatic.mSelectedRoute;
if (selectedRoute == null) return;
@@ -752,6 +802,9 @@
newRoute.mEnabled = available;
newRoute.mName = display.getFriendlyDisplayName();
+
+ newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
+ sStatic.getAllPresentationDisplays());
return newRoute;
}
@@ -827,6 +880,27 @@
return null;
}
+ private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
+ if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ if (route.mDeviceAddress != null) {
+ // Find the indicated Wifi display by its address.
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_WIFI
+ && route.mDeviceAddress.equals(display.getAddress())) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
+ // Choose the first presentation display from the list.
+ return displays[0];
+ }
+ }
+ return null;
+ }
+
/**
* Information about a media route.
*/
@@ -845,6 +919,7 @@
int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
int mPlaybackStream = AudioManager.STREAM_MUSIC;
VolumeCallbackInfo mVcb;
+ Display mPresentationDisplay;
String mDeviceAddress;
boolean mEnabled = true;
@@ -1116,6 +1191,38 @@
}
/**
+ * Gets the {@link Display} that should be used by the application to show
+ * a {@link android.app.Presentation} on an external display when this route is selected.
+ * Depending on the route, this may only be valid if the route is currently
+ * selected.
+ * <p>
+ * The preferred presentation display may change independently of the route
+ * being selected or unselected. For example, the presentation display
+ * of the default system route may change when an external HDMI display is connected
+ * or disconnected even though the route itself has not changed.
+ * </p><p>
+ * This method may return null if there is no external display associated with
+ * the route or if the display is not ready to show UI yet.
+ * </p><p>
+ * The application should listen for changes to the presentation display
+ * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
+ * show or dismiss its {@link android.app.Presentation} accordingly when the display
+ * becomes available or is removed.
+ * </p><p>
+ * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
+ * </p>
+ *
+ * @return The preferred presentation display to use when this route is
+ * selected or null if none.
+ *
+ * @see #ROUTE_TYPE_LIVE_VIDEO
+ * @see android.app.Presentation
+ */
+ public Display getPresentationDisplay() {
+ return mPresentationDisplay;
+ }
+
+ /**
* @return true if this route is enabled and may be selected
*/
public boolean isEnabled() {
@@ -1156,9 +1263,11 @@
@Override
public String toString() {
String supportedTypes = typesToString(getSupportedTypes());
- return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
- " category=" + getCategory() +
- " supportedTypes=" + supportedTypes + "}";
+ return getClass().getSimpleName() + "{ name=" + getName() +
+ ", status=" + getStatus() +
+ ", category=" + getCategory() +
+ ", supportedTypes=" + supportedTypes +
+ ", presentationDisplay=" + mPresentationDisplay + "}";
}
}
@@ -1853,6 +1962,21 @@
* @param info The route with altered volume
*/
public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
+
+ /**
+ * Called when a route's presentation display changes.
+ * <p>
+ * This method is called whenever the route's presentation display becomes
+ * available, is removes or has changes to some of its properties (such as its size).
+ * </p>
+ *
+ * @param router the MediaRouter reporting the event
+ * @param info The route whose presentation display changed
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ */
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ }
}
/**
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 02b5326..cfe70dc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -214,7 +214,7 @@
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
- android:icon="@mipmap/ic_daydreams"
+ android:icon="@mipmap/ic_launcher_dreams"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
deleted file mode 100644
index 56cbac1..0000000
--- a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
index a335d6d..37185f3 100644
--- a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
deleted file mode 100644
index ea3d991..0000000
--- a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
index ef2e27b..1993b0d 100644
--- a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
deleted file mode 100644
index ddc7f66..0000000
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
index 7b42cb4..c92b681 100644
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 69355d5..e5cc356 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skerm is in landskapsoriëntasie gesluit."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skerm is in portretoriëntasie gesluit."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Slaap nou"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Vliegtuigmodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 735da78..57ad48a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/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="6170089063982549905">"አሁን ተኛ"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"የቀን ህልም"</string>
<string name="ethernet_label" msgid="7967563676324087464">"ኤተርኔት"</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-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index da78d29..c40e26d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/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="6170089063982549905">"السكون الآن"</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-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 79bc53a..a4b4b73 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -176,7 +176,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Засыпай"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<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-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 59ba693..8876a38 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -174,7 +174,8 @@
<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="6170089063982549905">"В спящ режим сега"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<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-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bf10ce2..8d5cf36 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -176,7 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla està bloquejada en orientació horitzontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla està bloquejada en orientació vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Entra en mode repòs"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Somnis despert"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode d\'avió"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"S\'està carregant, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5e632fa..a623612 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -176,7 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamčena v orientaci na šířku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamčena v orientaci na výšku."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Spát nyní"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Spořič obrazovky"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Režim V letadle"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nabíjení, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 671f292..ddb6265 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skærmen er nu låst i stående retning."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Gå i dvale nu"</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">"Flytilstand"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Oplader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 3e767a0..fc18361 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -176,7 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildschirm bleibt im Querformat."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildschirm bleibt im Hochformat."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Ruhemodus ein"</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">"Flugmodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Lädt, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0665020..abe6082 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -176,7 +176,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="6170089063982549905">"Ενεργ. αναστ. λειτ."</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">"Λειτουργία πτήσης"</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-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 906b5c6..890e05e 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Screen is locked in portrait orientation."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Sleep Now"</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">"Aeroplane mode"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Charging, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 9bbd5d7..8abf3a8 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -176,7 +176,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Activar suspensión"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<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_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 90f93b2..79f0bb0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Suspender"</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 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>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d77b26a..5339006 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekraan on lukustatud horisontaalsuunas."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekraan on lukustatud vertikaalsuunas."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Nüüd unerežiimi"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Unistus"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lennurežiim"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laadimine, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 6accc9f..aa80148 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/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="6170089063982549905">"اکنون خواب"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"رویاپردازی"</string>
<string name="ethernet_label" msgid="7967563676324087464">"اترنت"</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-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3bc0371..d4b82f0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ruutu on lukittu vaakasuuntaan."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ruutu on lukittu pystysuuntaan."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Virransäästötilaan"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Unelmoi"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lentokonetila"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Ladataan (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 02eb529..e547478 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -176,7 +176,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"L\'écran est verrouillé en mode portrait."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Mettre en veille"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode avion"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"En charge (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e2bc2c3..cdcda79 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/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="6170089063982549905">"अभी निष्क्रिय करें"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"ईथरनेट"</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-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e318120..cfa5d54 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaključan u pejzažnoj orijentaciji."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaključan u portretnoj orijentaciji."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Miruj sad"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Sanjarenje"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Način rada u zrakoplovu"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Puni se, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3dafd2b..27bf2f5 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A képernyő zárolva van fekvő tájolásban."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A képernyő zárolva van álló tájolásban."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Alvó mód"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Álmodozás"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Repülőgép üzemmód"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Töltés (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c26cdce..baa7b221 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Tidur Sekarang"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode pesawat"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengisi baterai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e9ac98f..812d1ac 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -176,7 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Lo schermo è bloccato in orientamento orizzontale."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Lo schermo è bloccato in orientamento verticale."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Sospendi ora"</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">"Modalità aereo"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0481d63..150375d 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="6170089063982549905">"עבור לשינה עכשיו"</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">"מצב טיסה"</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-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 1b89e13..c427ac4 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -176,7 +176,8 @@
<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="6170089063982549905">"スリープ開始"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"イーサネット"</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-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 71d305f..deafa45 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -174,7 +174,8 @@
<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="6170089063982549905">"절전 모드로 전환"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"이더넷"</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-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 76de197..02d0c5b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Užrakintas ekranas yra horizontalios orientacijos."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Užrakintas ekranas yra vertikalios orientacijos."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Įj. miego rež. dabar"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Svajonė"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Eternetas"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lėktuvo režimas"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Įkraunama, <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 21299b2..57e70c8 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Ieslēgt miega režīmu"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Ekrānsaudzētājs"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Tīkls Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lidojuma režīms"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Notiek uzlāde, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 01751b0..7ce5b62 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrin dikunci dalam orientasi landskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrin dikunci dalam orientasi potret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Tidur Sekarang"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod kapal terbang"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengecas, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ccba8e5..abd7f84 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skjermen er låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skjermen er låst i stående retning."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Aktiver hvilemodus"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flymodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Lader: <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9978b76..c055aec 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Het scherm is nu vergrendeld in liggende stand."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Het scherm is nu vergrendeld in staande stand."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Nu slapen"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Dagdroom"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Vliegmodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Opladen, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 98f13c7..a05aa8f 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran jest zablokowany w orientacji poziomej."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran jest zablokowany w orientacji pionowej."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Zaśnij teraz"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Śnij na jawie"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Tryb samolotowy"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 639201c..1ca085c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -174,16 +174,16 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"O ecrã está bloqueado na orientação horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"O ecrã está bloqueado na orientação vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Em Suspensão Agora"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Sonho"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avião"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"A carregar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Carregada"</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> Dispositivos)"</string>
- <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Desat."</string>
+ <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desat."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rodar Automat."</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rodar automat."</string>
<string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotação Bloqueada"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de Introdução"</string>
<string name="quick_settings_location_label" msgid="3292451598267467545">"Localização em utilização"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9eeb01b..68f08dd 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -176,7 +176,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A tela está bloqueada na orientação retrato."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Suspender"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<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_battery_charging_label" msgid="490074774465309209">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index ef770a0..606160b 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -318,7 +318,7 @@
<skip />
<!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
<skip />
- <!-- no translation found for start_dreams (6170089063982549905) -->
+ <!-- no translation found for start_dreams (7219575858348719790) -->
<skip />
<!-- no translation found for ethernet_label (7967563676324087464) -->
<skip />
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 616ddb7..085f607 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ecranul este blocat în orientarea de tip peisaj."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ecranul este blocat în orientarea de tip portret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Activaţi mod inactiv"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod Avion"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Se încarcă, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6e10de4..2596f88 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -176,7 +176,8 @@
<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="6170089063982549905">"Спящий режим"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<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-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 9151512..ea70c10 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -176,7 +176,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamknutá v orientácii na šírku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamknutá v orientácii na výšku."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Spať"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Režim V lietadle"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nabíjanie, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7be851c..fb8812a 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Stanje pripravljenosti"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Način za letalo"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Polnjenje, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 9d9da87..42ab38a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/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="6170089063982549905">"Спавај одмах"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Сањарење"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Етернет"</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-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index bae649d..7bf9d4c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildskärmens riktning är nu låst i liggande format."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildskärmens riktning är nu låst i stående format."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Viloläge nu"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Dagdröm"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flygplansläge"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laddar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3b29b39..fba3da5 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -172,7 +172,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrini imefungwa sasa katika uelekezo wa mandhari."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrini imefungwa katika uelekeo wa picha."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Lala Sasa"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Ndoto ya mchana"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modi ya ndege"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Inachaji, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 668504e..2d6f5f2 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -174,7 +174,8 @@
<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="6170089063982549905">"เข้าสู่โหมดสลีปเลย"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 312d8fd..52b3758 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Naka-lock ang screen sa pahigang oryentasyon."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Naka-lock ang screen sa patayong oryentasyon."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Mag-sleep Ngayon"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Airplane mode"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nagcha-charge, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 5012bc5..08111ef 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Şimdi Uyu"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Uçak modu"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Şarj oluyor, <xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ba36be6..71a2bca 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/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="6170089063982549905">"Перейти в режим сну"</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-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index ce90afc..87ee67b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -174,7 +174,8 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Ngủ bây giờ"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Chế độ trên máy bay"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ba0d9c8..45be2e9 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -176,7 +176,8 @@
<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">"果冻豆大乱舞"</string>
- <string name="start_dreams" msgid="6170089063982549905">"立即休眠"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"以太网"</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-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index a77dd8c..d64ca20 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -176,7 +176,8 @@
<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="6170089063982549905">"立即休眠"</string>
+ <!-- no translation found for start_dreams (7219575858348719790) -->
+ <skip />
<string name="ethernet_label" msgid="7967563676324087464">"乙太網路"</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-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8ad1881..3b772be 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"I-BeanFlinger"</string>
- <string name="start_dreams" msgid="6170089063982549905">"Lala manje"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Ukuphupha emini"</string>
<string name="ethernet_label" msgid="7967563676324087464">"I-Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Isimo sendiza"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Iyashaja <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
index a367367..39e4727 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui;
import android.service.dreams.DreamService;
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 0a7dd7c..9da883a 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -401,6 +401,7 @@
Throwable exception = null;
try {
mWallpaperManager.forgetLoadedWallpaper(); // force reload
+ mBackground = null;
mBackground = mWallpaperManager.getBitmap();
} catch (RuntimeException e) {
exception = e;
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
index 1f00bc1..0dd6d92 100644
--- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -43,7 +43,7 @@
| Intent.FLAG_ACTIVITY_NEW_TASK);
Intent resultIntent = new Intent();
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_daydreams));
+ Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_dreams));
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams));
setResult(RESULT_OK, resultIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
index 5dd45a4..735ee25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index eef5446..983328d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -55,6 +55,9 @@
final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
+ // slippery nav bar when everything is disabled, e.g. during setup
+ final static boolean SLIPPERY_WHEN_DISABLED= true;
+
final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen
protected IStatusBarService mBarService;
@@ -237,7 +240,9 @@
final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0);
final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
- setSlippery(disableHome && disableRecent && disableBack);
+ if (SLIPPERY_WHEN_DISABLED) {
+ setSlippery(disableHome && disableRecent && disableBack && disableSearch);
+ }
if (!mScreenOn && mCurrentView != null) {
ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c9a137c..4b07b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.phone;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
index 241ac3e..8a54347 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.phone;
import android.content.Context;
@@ -63,4 +79,4 @@
public void setBar(PanelBar panelBar) {
mBar = panelBar;
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6184e30..7035006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.phone;
import android.animation.ObjectAnimator;
@@ -320,7 +336,7 @@
final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
if (deltaY < mFlingGestureMinDistPx
|| vel < mFlingExpandMinVelocityPx
- || mJustPeeked) {
+ ) {
vel = 0;
}
@@ -328,10 +344,8 @@
vel = -vel;
}
- if (DEBUG) LOG("gesture: dy=%f vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
+ if (DEBUG) LOG("gesture: dy=%f vel=(%f,%f) vlinear=%f",
deltaY,
- mVelocityTracker.getXVelocity(),
- mVelocityTracker.getYVelocity(),
xVel, yVel,
vel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
index 7a2f25a..225ebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
new file mode 100644
index 0000000..db7e231
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
@@ -0,0 +1,219 @@
+/*
+ * 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.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import com.android.internal.policy.impl.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
+
+public class CameraWidgetFrame extends KeyguardWidgetFrame {
+ private static final String TAG = CameraWidgetFrame.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final int WIDGET_ANIMATION_DURATION = 250;
+
+ interface Callbacks {
+ void onLaunchingCamera();
+ void onCameraLaunched();
+ }
+
+ private final Handler mHandler = new Handler();
+ private final KeyguardActivityLauncher mActivityLauncher;
+ private final Callbacks mCallbacks;
+
+ private View mWidgetView;
+ private long mLaunchCameraStart;
+ private boolean mRendered;
+
+ private final Runnable mLaunchCameraRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLaunchCameraStart = SystemClock.uptimeMillis();
+ mActivityLauncher.launchCamera();
+ }};
+
+ private final Runnable mRenderRunnable = new Runnable() {
+ @Override
+ public void run() {
+ render();
+ }};
+
+ private CameraWidgetFrame(Context context, Callbacks callbacks,
+ KeyguardActivityLauncher activityLauncher) {
+ super(context);
+
+ mCallbacks = callbacks;
+ mActivityLauncher = activityLauncher;
+ }
+
+ 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 widgetView = widgetInfo.layoutId > 0 ?
+ inflateWidgetView(context, widgetInfo) :
+ inflateGenericWidgetView(context);
+ if (widgetView == null)
+ return null;
+
+ ImageView preview = new ImageView(context);
+ preview.setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ preview.setScaleType(ScaleType.FIT_CENTER);
+ CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher);
+ cameraWidgetFrame.addView(preview);
+ cameraWidgetFrame.mWidgetView = widgetView;
+ return cameraWidgetFrame;
+ }
+
+ private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
+ 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) {
+ 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;
+ }
+
+ public void render() {
+ if (mRendered) return;
+
+ try {
+ int width = getRootView().getWidth();
+ int height = getRootView().getHeight();
+ if (DEBUG) Log.d(TAG, String.format("render [%sx%s] %s",
+ width, height, Integer.toHexString(hashCode())));
+ if (width == 0 || height == 0) {
+ return;
+ }
+ Bitmap offscreen = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(offscreen);
+ mWidgetView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ mWidgetView.layout(0, 0, width, height);
+ mWidgetView.draw(c);
+ ((ImageView)getChildAt(0)).setImageBitmap(offscreen);
+ mRendered = true;
+ } catch (Throwable t) {
+ Log.w(TAG, "Error rendering camera widget", t);
+ removeAllViews();
+ View genericView = inflateGenericWidgetView(mContext);
+ addView(genericView);
+ }
+ }
+
+ private void transitionToCamera() {
+ int startWidth = getChildAt(0).getWidth();
+ int startHeight = getChildAt(0).getHeight();
+
+ int finishWidth = getRootView().getWidth();
+ int finishHeight = getRootView().getHeight();
+
+ float scaleX = (float) finishWidth / startWidth;
+ float scaleY = (float) finishHeight / startHeight;
+
+ float scale = Math.max(scaleX, scaleY);
+ animate()
+ .scaleX(scale)
+ .scaleY(scale)
+ .setDuration(WIDGET_ANIMATION_DURATION)
+ .withEndAction(mLaunchCameraRunnable)
+ .start();
+ mCallbacks.onLaunchingCamera();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (!hasWindowFocus) {
+ if (mLaunchCameraStart > 0) {
+ long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+ if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+ mLaunchCameraStart = 0;
+ onCameraLaunched();
+ }
+ }
+ }
+
+ @Override
+ public void onActive(boolean isActive) {
+ if (isActive) {
+ mHandler.post(new Runnable(){
+ @Override
+ public void run() {
+ transitionToCamera();
+ }});
+ } else {
+ reset();
+ }
+ }
+
+ private void onCameraLaunched() {
+ reset();
+ mCallbacks.onCameraLaunched();
+ }
+
+ private void reset() {
+ animate().cancel();
+ setScaleX(1);
+ setScaleY(1);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mHandler.post(mRenderRunnable);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
new file mode 100644
index 0000000..605a738
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
@@ -0,0 +1,92 @@
+/*
+ * 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();
+
+ /**
+ * 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
new file mode 100644
index 0000000..020fdba
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
@@ -0,0 +1,80 @@
+/*
+ * 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();
+
+ if (Math.sqrt(Math.pow(mDownX - x, 2) + Math.pow(mDownY - y, 2)) > mScaledTouchSlop) {
+ cancelLongPress();
+ }
+ }
+
+ public void cancelLongPress() {
+ mHasPerformedLongPress = false;
+ if (mPendingCheckForLongPress != null) {
+ mView.removeCallbacks(mPendingCheckForLongPress);
+ mPendingCheckForLongPress = null;
+ }
+ }
+
+ public boolean hasPerformedLongPress() {
+ return mHasPerformedLongPress;
+ }
+}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
index 203ba3c..cab4ed9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
@@ -23,7 +23,6 @@
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.Button;
import com.android.internal.telephony.IccCardConstants.State;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 3fe16cf..000acb1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -147,7 +147,7 @@
public boolean stop() {
if (DEBUG) Log.d(TAG, "stop()");
if (mHandler.getLooper() != Looper.myLooper()) {
- Log.e(TAG, "stop() called off of the UI thread");
+ Log.e(TAG, "stop() called from non-UI thread");
}
boolean mWasRunning = mIsRunning;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
new file mode 100644
index 0000000..2eb10fe
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
@@ -0,0 +1,237 @@
+/*
+ * 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.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 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 void resetState();
+
+ @Override
+ protected void onFinishInflate() {
+ // We always set a dummy NavigationManager to avoid null checks
+ mSecurityMessageDisplay = new KeyguardNavigationManager(null);
+
+ mLockPatternUtils = new LockPatternUtils(mContext);
+
+ mPasswordEntry = (TextView) findViewById(R.id.passwordEntry);
+ mPasswordEntry.setOnEditorActionListener(this);
+ mPasswordEntry.addTextChangedListener(this);
+
+ // 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);
+ }
+ }
+ });
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ // send focus to the password field
+ return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ 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(R.string.kg_wrong_pin, 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() {
+ 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() {
+ 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) {
+ }
+
+ @Override
+ public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
+ mSecurityMessageDisplay = display;
+ reset();
+ }
+
+ // 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/KeyguardAccountView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
index ebca4ac..ea7a8e7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
@@ -321,5 +321,9 @@
mSecurityMessageDisplay = display;
reset();
}
+
+ @Override
+ public void showUsabilityHint() {
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
new file mode 100644
index 0000000..a224a42
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
@@ -0,0 +1,174 @@
+/*
+ * 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.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.WindowManager;
+
+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() {
+ 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);
+ } else {
+ launchActivity(SECURE_CAMERA_INTENT, true);
+ }
+ } else {
+ // Launch the normal camera
+ launchActivity(INSECURE_CAMERA_INTENT, false);
+ }
+ }
+
+ /**
+ * 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}
+ */
+ public void launchActivity(final Intent intent, boolean showsWhileLocked) {
+ final Context context = getContext();
+ 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) try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ Log.w(TAG, "can't dismiss keyguard on launch");
+ }
+ try {
+ context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ } 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.setOnDismissRunnable(new Runnable() {
+ @Override
+ public void run() {
+ context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ callback.dismiss(false);
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..29124c4
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
@@ -0,0 +1,161 @@
+/*
+ * 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) {
+ Log.i("KFD", "scale: " + 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
index 04ab871..4aa6b05 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -39,6 +39,9 @@
private View mFaceUnlockAreaView;
private ImageButton mCancelButton;
+ private boolean mIsShowing = false;
+ private final Object mIsShowingLock = new Object();
+
public KeyguardFaceUnlockView(Context context) {
this(context, null);
}
@@ -112,6 +115,7 @@
}
private void initializeBiometricUnlockView() {
+ if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()");
mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
if (mFaceUnlockAreaView != null) {
mBiometricUnlock = new FaceUnlock(mContext);
@@ -129,9 +133,9 @@
}
/**
- * Starts the biometric unlock if it should be started based on a number of factors including
- * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
- * unlock area.
+ * 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()");
@@ -142,12 +146,25 @@
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;
+ }
+
// TODO: Some of these conditions are handled in KeyguardSecurityModel and may not be
// necessary here.
if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
&& !monitor.getMaxBiometricUnlockAttemptsReached()
- && !backupIsTimedOut
- && powerManager.isScreenOn()) {
+ && !backupIsTimedOut) {
mBiometricUnlock.start();
} else {
mBiometricUnlock.stopAndShowBackup();
@@ -161,7 +178,9 @@
public void onPhoneStateChanged(int phoneState) {
if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")");
if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
- mBiometricUnlock.stopAndShowBackup();
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stopAndShowBackup();
+ }
}
}
@@ -174,10 +193,33 @@
// No longer required; static value set by KeyguardViewMediator
// mLockPatternUtils.setCurrentUser(userId);
}
+
+ @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 setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
+ mSecurityMessageDisplay = display;
+ }
+
+ @Override
+ public void showUsabilityHint() {
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index b86e5b8..b846729 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -35,7 +35,9 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
@@ -47,7 +49,6 @@
import android.view.animation.AnimationUtils;
import android.widget.RemoteViews.OnClickHandler;
import android.widget.TextView;
-import android.widget.ViewFlipper;
import com.android.internal.R;
import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -60,7 +61,7 @@
private static final String TAG = "KeyguardViewHost";
// Use this to debug all of keyguard
- public static boolean DEBUG;
+ public static boolean DEBUG = KeyguardViewMediator.DEBUG;
// also referenced in SecuritySettings.java
static final int APPWIDGET_HOST_ID = 0x4B455947;
@@ -71,9 +72,8 @@
private static final int TRANSPORT_VISIBLE = 2;
private AppWidgetHost mAppWidgetHost;
- private KeyguardWidgetRegion mAppWidgetRegion;
private KeyguardWidgetPager mAppWidgetContainer;
- private ViewFlipper mSecurityViewContainer;
+ private KeyguardSecurityViewFlipper mSecurityViewContainer;
private KeyguardSelectorView mKeyguardSelectorView;
private KeyguardTransportControlView mTransportControl;
private boolean mEnableMenuKey;
@@ -87,6 +87,7 @@
private LockPatternUtils mLockPatternUtils;
private KeyguardSecurityModel mSecurityModel;
+ private KeyguardViewStateManager mViewStateManager;
private Rect mTempRect = new Rect();
private int mTransportState = TRANSPORT_GONE;
@@ -101,6 +102,7 @@
void hideSecurityView(int duration);
void showSecurityView();
void showUnlockHint();
+ void userActivity();
}
public KeyguardHostView(Context context) {
@@ -151,21 +153,41 @@
@Override
protected void onFinishInflate() {
- mAppWidgetRegion = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
- mAppWidgetRegion.setVisibility(VISIBLE);
- mAppWidgetRegion.setCallbacks(mWidgetCallbacks);
-
+ // Grab instances of and make any necessary changes to the main layouts. Create
+ // view state manager and wire up necessary listeners / callbacks.
mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
- mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
- mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+ mAppWidgetContainer.setVisibility(VISIBLE);
+ mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
+ mAppWidgetContainer.setMinScale(0.5f);
addDefaultWidgets();
- updateSecurityViews();
- setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+ addWidgetsFromSettings();
- if (KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
- showPrimarySecurityScreen(false);
+ mViewStateManager = new KeyguardViewStateManager();
+ SlidingChallengeLayout slider =
+ (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ if (slider != null) {
+ slider.setOnChallengeScrolledListener(mViewStateManager);
}
+ mAppWidgetContainer.setViewStateManager(mViewStateManager);
+ mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
+
+ mViewStateManager.setPagedView(mAppWidgetContainer);
+ mViewStateManager.setChallengeLayout(slider != null ? slider :
+ (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
+ mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
+ mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+ mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
+
+ mViewStateManager.showUsabilityHints();
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+ }
+
+ showPrimarySecurityScreen(false);
+
+ updateSecurityViews();
}
private void updateSecurityViews() {
@@ -195,22 +217,9 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAppWidgetHost.startListening();
- // TODO: Re-enable when we have layouts that can support a better variety of widgets.
- // maybePopulateWidgets();
- disableStatusViewInteraction();
post(mSwitchPageRunnable);
}
- private void disableStatusViewInteraction() {
- // Disable all user interaction on status view. This is done to prevent falsing in the
- // pocket from triggering useractivity and prevents 3rd party replacement widgets
- // from responding to user interaction while in this position.
- View statusView = findViewById(R.id.keyguard_status_view);
- if (statusView instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
- }
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -221,12 +230,12 @@
return mAppWidgetHost;
}
- void addWidget(AppWidgetHostView view) {
- mAppWidgetContainer.addWidget(view);
+ void addWidget(AppWidgetHostView view, int pageIndex) {
+ mAppWidgetContainer.addWidget(view, pageIndex);
}
- private KeyguardWidgetRegion.Callbacks mWidgetCallbacks
- = new KeyguardWidgetRegion.Callbacks() {
+ private KeyguardWidgetPager.Callbacks mWidgetCallbacks
+ = new KeyguardWidgetPager.Callbacks() {
@Override
public void userActivity() {
if (mViewMediatorCallback != null) {
@@ -240,14 +249,22 @@
mViewMediatorCallback.onUserActivityTimeoutChanged();
}
}
+
+ @Override
+ public void onPageSwitch(int newPageIndex) {
+ if (!isCameraOrAdd(newPageIndex)) {
+ if (DEBUG) Log.d(TAG, "Setting sticky widget index: " + newPageIndex);
+ mLockPatternUtils.setStickyWidgetIndex(newPageIndex);
+ }
+ }
};
@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 (mAppWidgetRegion != null) {
- return mAppWidgetRegion.getUserActivityTimeout();
+ if (mAppWidgetContainer != null) {
+ return mAppWidgetContainer.getUserActivityTimeout();
}
return -1;
}
@@ -317,13 +334,11 @@
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
-
- case Password: {
- final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
- messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
- : R.string.kg_too_many_failed_password_attempts_dialog_message;
- }
+ 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;
}
@@ -418,7 +433,8 @@
void showPrimarySecurityScreen(boolean turningOff) {
SecurityMode securityMode = mSecurityModel.getSecurityMode();
if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
+ if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()
+ && !KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
// If we're not turning off, then allow biometric alternate.
// We'll reload it when the device comes back on.
securityMode = mSecurityModel.getAlternateFor(securityMode);
@@ -465,6 +481,7 @@
switch (mCurrentSecuritySelection) {
case Pattern:
case Password:
+ case PIN:
case Account:
case Biometric:
finish = true;
@@ -504,6 +521,8 @@
if (mViewMediatorCallback != null) {
mViewMediatorCallback.keyguardDone(true);
}
+ } else {
+ mViewStateManager.showBouncer(true);
}
}
@@ -607,7 +626,6 @@
break;
}
}
- boolean simPukFullScreen = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
int layoutId = getLayoutIdFor(securityMode);
if (view == null && layoutId != 0) {
final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -624,14 +642,9 @@
if (navigationText != null) {
view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
} else {
- view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
- }
- }
-
- if (securityMode == SecurityMode.SimPin || securityMode == SecurityMode.SimPuk ||
- securityMode == SecurityMode.Account) {
- if (simPukFullScreen) {
- mAppWidgetRegion.setVisibility(View.GONE);
+ if (mKeyguardStatusViewManager != null) {
+ view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
+ }
}
}
@@ -658,6 +671,15 @@
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);
+
// Emulate Activity life cycle
if (oldView != null) {
oldView.onPause();
@@ -698,7 +720,7 @@
@Override
public void onScreenTurnedOn() {
- if (DEBUG) Log.d(TAG, "screen on");
+ if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
showPrimarySecurityScreen(false);
getSecurityView(mCurrentSecuritySelection).onResume();
@@ -706,18 +728,23 @@
// layout is blank but forcing a layout causes it to reappear (e.g. with with
// hierarchyviewer).
requestLayout();
+
+ if (mViewStateManager != null) {
+ mViewStateManager.showUsabilityHints();
+ }
}
@Override
public void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "screen off");
+ if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode()));
showPrimarySecurityScreen(true);
getSecurityView(mCurrentSecuritySelection).onPause();
}
@Override
public void show() {
- onScreenTurnedOn();
+ if (DEBUG) Log.d(TAG, "show()");
+ showPrimarySecurityScreen(false);
}
private boolean isSecure() {
@@ -726,6 +753,7 @@
case Pattern:
return mLockPatternUtils.isLockPatternEnabled();
case Password:
+ case PIN:
return mLockPatternUtils.isLockPasswordEnabled();
case SimPin:
case SimPuk:
@@ -760,6 +788,7 @@
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) {
@@ -776,6 +805,7 @@
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;
@@ -789,6 +819,7 @@
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;
@@ -799,23 +830,99 @@
}
}
- private void addWidget(int appId) {
+ private void addWidget(int appId, int pageIndex) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
if (appWidgetInfo != null) {
AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
- addWidget(view);
+ addWidget(view, pageIndex);
} else {
- Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
+ Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
+ mLockPatternUtils.removeAppWidget(appId);
}
}
+ private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
+ new CameraWidgetFrame.Callbacks() {
+ @Override
+ public void onLaunchingCamera() {
+ SlidingChallengeLayout slider = locateSlider();
+ if (slider != null) {
+ slider.showHandle(false);
+ }
+ }
+
+ @Override
+ public void onCameraLaunched() {
+ SlidingChallengeLayout slider = locateSlider();
+ if (slider != null) {
+ slider.showHandle(true);
+ slider.showChallenge(true);
+ }
+ View v = mAppWidgetContainer.getChildAt(mAppWidgetContainer.getCurrentPage());
+ if (v instanceof CameraWidgetFrame) {
+ mAppWidgetContainer.scrollLeft();
+ }
+ }
+
+ private SlidingChallengeLayout locateSlider() {
+ return (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ }
+ };
+
+ private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+ @Override
+ Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }};
+
private void addDefaultWidgets() {
LayoutInflater inflater = LayoutInflater.from(mContext);
- inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
- inflateAndAddUserSelectorWidgetIfNecessary();
+ View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
+ mAppWidgetContainer.addWidget(addWidget);
+ if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
+ View cameraWidget =
+ CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
+ if (cameraWidget != null) {
+ mAppWidgetContainer.addWidget(cameraWidget);
+ }
+ }
+
+ View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
+ addWidgetButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCallback.setOnDismissRunnable(new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ mContext.startActivityAsUser(intent,
+ new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ mCallback.dismiss(false);
+ }
+ });
+
+ enableUserSelectorIfNecessary();
initializeTransportControl();
}
@@ -857,12 +964,15 @@
});
}
- mKeyguardStatusViewManager = ((KeyguardStatusView)
- findViewById(R.id.keyguard_status_view_face_palm)).getManager();
+ KeyguardStatusView ksv = (KeyguardStatusView)
+ findViewById(R.id.keyguard_status_view_face_palm);
+ if (ksv != null) {
+ mKeyguardStatusViewManager = ksv.getManager();
+ }
}
- private void maybePopulateWidgets() {
+ private void addWidgetsFromSettings() {
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (dpm != null) {
@@ -874,27 +984,29 @@
}
}
- // Replace status widget if selected by user in Settings
- int statusWidgetId = mLockPatternUtils.getStatusWidget();
- if (statusWidgetId != -1) {
- addWidget(statusWidgetId);
- View newStatusWidget = mAppWidgetContainer.getChildAt(
- mAppWidgetContainer.getChildCount() - 1);
-
- int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view);
- mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition);
-
- // Re-add new status widget at position of old one
- mAppWidgetContainer.removeView(newStatusWidget);
- newStatusWidget.setId(R.id.keyguard_status_view);
- mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
+ View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
+ int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
+ // This shouldn't happen, but just to be safe!
+ if (addPageIndex < 0) {
+ addPageIndex = 0;
}
// Add user-selected widget
- final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
- for (int i = 0; i < widgets.length; i++) {
- if (widgets[i] != -1) {
- addWidget(widgets[i]);
+ 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) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
+ mAppWidgetContainer.addWidget(statusWidget, addPageIndex + 1);
+ } 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], addPageIndex + 1);
+ }
}
}
}
@@ -957,47 +1069,77 @@
}
private void showAppropriateWidgetPage() {
-
- // The following sets the priority for showing widgets. Transport should be shown if
- // music is playing, followed by the multi-user widget if enabled, followed by the
- // status widget.
- final int pageToShow;
- if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+ boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+ if (music) {
mTransportState = TRANSPORT_VISIBLE;
- pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
- } else {
- UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
- final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
- if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
- pageToShow = multiUserPosition;
- } else {
- final View statusView = findViewById(R.id.keyguard_status_view);
- pageToShow = mAppWidgetContainer.indexOfChild(statusView);
- }
- if (mTransportState == TRANSPORT_VISIBLE) {
- mTransportState = TRANSPORT_INVISIBLE;
- }
+ } else if (mTransportState == TRANSPORT_VISIBLE) {
+ mTransportState = TRANSPORT_INVISIBLE;
}
+ int pageToShow = getAppropriateWidgetPage();
mAppWidgetContainer.setCurrentPage(pageToShow);
}
- private void inflateAndAddUserSelectorWidgetIfNecessary() {
+ private boolean isCameraOrAdd(int pageIndex) {
+ View v = mAppWidgetContainer.getChildAt(pageIndex);
+ return v.getId() == R.id.keyguard_add_widget || v instanceof CameraWidgetFrame;
+ }
+
+ private int getAppropriateWidgetPage() {
+ // assumes at least one widget (besides camera + add)
+
+ boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+ // if music playing, show transport
+ if (music) {
+ if (DEBUG) Log.d(TAG, "Music playing, show transport");
+ return mAppWidgetContainer.indexOfChild(mTransportControl);
+ }
+
+ // if multi-user applicable, show it
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
+ int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
+ if (multiUserPosition != -1 && userManager.getUsers(true).size() > 1) {
+ if (DEBUG) Log.d(TAG, "Multi-user applicable, show it");
+ return multiUserPosition;
+ }
+
+ // if we have a sticky widget, show it
+ int stickyWidgetIndex = mLockPatternUtils.getStickyWidgetIndex();
+ if (stickyWidgetIndex > -1
+ && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
+ && !isCameraOrAdd(stickyWidgetIndex)) {
+ if (DEBUG) Log.d(TAG, "Sticky widget found, show it");
+ return stickyWidgetIndex;
+ }
+
+ // if we have a status view, show it
+ View statusView = findViewById(R.id.keyguard_status_view);
+ int statusViewIndex = mAppWidgetContainer.indexOfChild(statusView);
+ if (statusViewIndex > -1) {
+ if (DEBUG) Log.d(TAG, "Status widget found, show it");
+ return mAppWidgetContainer.indexOfChild(statusView);
+ }
+
+ // else the right-most (except for camera)
+ int rightMost = mAppWidgetContainer.getChildCount() - 1;
+ if (mAppWidgetContainer.getChildAt(rightMost) instanceof CameraWidgetFrame) {
+ rightMost--;
+ }
+ if (DEBUG) Log.d(TAG, "Show right-most");
+ return rightMost;
+ }
+
+ private void enableUserSelectorIfNecessary() {
// if there are multiple users, we need to add the multi-user switcher widget to the
// keyguard.
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
List<UserInfo> users = mUm.getUsers(true);
if (users.size() > 1) {
- KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
- LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
- mAppWidgetContainer, false);
-
- // add the switcher in the first position
- mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
- KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
- userSwitcher.getChildAt(0);
-
+ KeyguardMultiUserSelectorView multiUser =
+ (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
+ multiUser.setVisibility(View.VISIBLE);
+ multiUser.addUsers(mUm.getUsers(true));
UserSwitcherCallback callback = new UserSwitcherCallback() {
@Override
public void hideSecurityView(int duration) {
@@ -1012,10 +1154,16 @@
@Override
public void showUnlockHint() {
if (mKeyguardSelectorView != null) {
- mKeyguardSelectorView.ping();
+ mKeyguardSelectorView.showUsabilityHint();
}
}
+ @Override
+ public void userActivity() {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.userActivity();
+ }
+ }
};
multiUser.setCallback(callback);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
new file mode 100644
index 0000000..0fc54cd
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/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.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/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 3c972bc..a21ebe3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -18,15 +18,18 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
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.graphics.drawable.Drawable;
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;
@@ -34,23 +37,38 @@
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 = 0.5f;
- private static final float ACTIVE_SCALE = 1.2f;
- private static final float ACTIVE_TEXT_BACGROUND_ALPHA = 0.5f;
- private static final float INACTIVE_TEXT_BACGROUND_ALPHA = 0f;
- private static int mActiveTextColor;
- private static int mInactiveTextColor;
+ 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;
-
- boolean mPressedStateLocked = false;
- boolean mTempPressedStateHolder = false;
+ private KeyguardCircleFramedDrawable mFramed;
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
KeyguardMultiUserSelectorView userSelector, UserInfo info) {
@@ -73,8 +91,29 @@
super(context, attrs, defStyle);
Resources res = mContext.getResources();
- mActiveTextColor = res.getColor(R.color.kg_multi_user_text_active);
- mInactiveTextColor = res.getColor(R.color.kg_multi_user_text_inactive);
+ 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) {
@@ -84,39 +123,52 @@
mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
mUserName = (TextView) findViewById(R.id.keyguard_user_name);
- mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
+ 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);
- setActive(false, false, 0, null);
mInit = false;
}
- public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
+ public void setActive(boolean active, boolean animate, final Runnable onComplete) {
if (mActive != active || mInit) {
mActive = active;
if (active) {
- KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
- parent.setTopChild(parent.indexOfChild(this));
+ KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent();
+ parent.setTopChild(this);
}
}
- updateVisualsForActive(mActive, animate, duration, true, onComplete);
+ updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete);
}
- void updateVisualsForActive(boolean active, boolean animate, int duration, boolean scale,
+ void updateVisualsForActive(boolean active, boolean animate, int duration,
final Runnable onComplete) {
- final float finalAlpha = active ? ACTIVE_ALPHA : INACTIVE_ALPHA;
- final float initAlpha = active ? INACTIVE_ALPHA : ACTIVE_ALPHA;
- final float finalScale = active && scale ? ACTIVE_SCALE : 1.0f;
- final float initScale = active ? 1.0f : ACTIVE_SCALE;
- final int finalTextBgAlpha = active ? (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255) :
- (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255);
- final int initTextBgAlpha = active ? (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255) :
- (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255);
- int textColor = active ? mActiveTextColor : mInactiveTextColor;
+ 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) {
+ if (animate && mTouched) {
ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
va.addUpdateListener(new AnimatorUpdateListener() {
@Override
@@ -124,12 +176,11 @@
float r = animation.getAnimatedFraction();
float scale = (1 - r) * initScale + r * finalScale;
float alpha = (1 - r) * initAlpha + r * finalAlpha;
- int textBgAlpha = (int) ((1 - r) * initTextBgAlpha + r * finalTextBgAlpha);
- setScaleX(scale);
- setScaleY(scale);
+ int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha);
+ mFramed.setScale(scale);
mUserImage.setAlpha(alpha);
- mUserName.setBackgroundColor(Color.argb(textBgAlpha, 0, 0, 0));
- mUserSelector.invalidate();
+ mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255));
+ mUserImage.invalidate();
}
});
va.addListener(new AnimatorListenerAdapter() {
@@ -143,37 +194,23 @@
va.setDuration(duration);
va.start();
} else {
- setScaleX(finalScale);
- setScaleY(finalScale);
+ mFramed.setScale(finalScale);
mUserImage.setAlpha(finalAlpha);
- mUserName.setBackgroundColor(Color.argb(finalTextBgAlpha, 0, 0, 0));
+ mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255));
if (onComplete != null) {
post(onComplete);
}
}
- }
- public void lockPressedState() {
- mPressedStateLocked = true;
- }
-
- public void resetPressedState() {
- mPressedStateLocked = false;
- post(new Runnable() {
- @Override
- public void run() {
- KeyguardMultiUserAvatar.this.setPressed(mTempPressedStateHolder);
- }
- });
+ mTouched = true;
}
@Override
public void setPressed(boolean pressed) {
- if (!mPressedStateLocked) {
+ if (!pressed || isClickable()) {
super.setPressed(pressed);
- updateVisualsForActive(pressed || mActive, false, 0, mActive, null);
- } else {
- mTempPressedStateHolder = pressed;
+ mFramed.setPressed(pressed);
+ mUserImage.invalidate();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index 246c255..728e87c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -20,26 +20,26 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.RemoteException;
-import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.WindowManagerGlobal;
+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 KeyguardSubdivisionLayout mUsersGrid;
+ private ViewGroup mUsersGrid;
private KeyguardMultiUserAvatar mActiveUserAvatar;
private KeyguardHostView.UserSwitcherCallback mCallback;
- private static final int SWITCH_ANIMATION_DURATION = 150;
private static final int FADE_OUT_ANIMATION_DURATION = 100;
public KeyguardMultiUserSelectorView(Context context) {
@@ -55,19 +55,18 @@
}
protected void onFinishInflate () {
- init();
+ mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid);
+ mUsersGrid.removeAllViews();
+ setClipChildren(false);
+ setClipToPadding(false);
+
}
public void setCallback(KeyguardHostView.UserSwitcherCallback callback) {
mCallback = callback;
}
- public void init() {
- mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
- mUsersGrid.removeAllViews();
- setClipChildren(false);
- setClipToPadding(false);
-
+ public void addUsers(Collection<UserInfo> userList) {
UserInfo activeUser;
try {
activeUser = ActivityManagerNative.getDefault().getCurrentUser();
@@ -75,17 +74,18 @@
activeUser = null;
}
- UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers(true));
+ 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;
+ mActiveUserAvatar.setActive(true, false, null);
+ } else {
+ uv.setActive(false, false, null);
}
}
- mActiveUserAvatar.setActive(true, false, 0, null);
}
Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
@@ -103,27 +103,57 @@
}
@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 (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
- avatar.lockPressedState();
- mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
- mActiveUserAvatar.setActive(false, true, SWITCH_ANIMATION_DURATION, new Runnable() {
- @Override
- public void run() {
- try {
- ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't switch user " + re);
+ 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);
+ mActiveUserAvatar.setActive(false, true, new Runnable() {
+ @Override
+ public void run() {
+ mActiveUserAvatar = avatar;
+ mActiveUserAvatar.setActive(true, true, new Runnable() {
+ @Override
+ public void run() {
+ 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
new file mode 100644
index 0000000..5cdf4d3
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
@@ -0,0 +1,104 @@
+/*
+ * 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 {
+
+ // 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.
+ private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+ // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
+ private static final boolean ENABLE_HIDE_KEYBOARD = false;
+
+ public KeyguardPINView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPINView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false);
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @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() {
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index be2dc8f..b6334f0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -16,70 +16,35 @@
package com.android.internal.policy.impl.keyguard;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-import java.util.List;
-
import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import com.android.internal.widget.PasswordEntryKeyboardView;
-
-import android.os.CountDownTimer;
-import android.os.SystemClock;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.text.method.TextKeyListener;
-import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.EditorInfo;
+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.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
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 a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
+ * Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends LinearLayout
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
- /** Delay in ms between updates to the "too many attempts" count down. */
- private static final long LOCKOUT_INTERVAL = 1000;
- /**
- * Delay in ms between updates to the "too many attempts" count down used
- * when accessibility is turned on. Less annoying than the shorter default
- * {@link #LOCKOUT_INTERVAL}.
- */
- private static final long ACCESSIBILITY_LOCKOUT_INTERVAL = 10000;
-
- private KeyguardSecurityCallback mCallback;
- private EditText mPasswordEntry;
- private LockPatternUtils mLockPatternUtils;
- private PasswordEntryKeyboardView mKeyboardView;
- private PasswordEntryKeyboardHelper mKeyboardHelper;
- private boolean mIsAlpha;
- private SecurityMessageDisplay mSecurityMessageDisplay;
-
- // 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.
- private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
-
- // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
- private static final boolean ENABLE_HIDE_KEYBOARD = false;
+ InputMethodManager mImm;
public KeyguardPasswordView(Context context) {
super(context);
@@ -89,110 +54,40 @@
super(context, attrs);
}
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
+ protected void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
+ mPasswordEntry.setEnabled(true);
}
@Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- if (hasWindowFocus) {
- reset();
- }
+ public boolean needsInput() {
+ return true;
}
- 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();
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ mImm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
- private void resetState() {
- mSecurityMessageDisplay.setMessage(
- mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions, false);
- mPasswordEntry.setEnabled(true);
- mKeyboardView.setEnabled(true);
+ @Override
+ public void onPause() {
+ super.onPause();
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@Override
protected void onFinishInflate() {
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
- mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one
-
- final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
- mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
- || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
- || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
-
- mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
- mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false,
- new int[] {
- R.xml.kg_password_kbd_numeric,
- com.android.internal.R.xml.password_kbd_qwerty,
- com.android.internal.R.xml.password_kbd_qwerty_shifted,
- com.android.internal.R.xml.password_kbd_symbols,
- com.android.internal.R.xml.password_kbd_symbols_shift
- }
- );
- mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+ super.onFinishInflate();
boolean imeOrDeleteButtonVisible = false;
- if (mIsAlpha) {
- // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
- mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
- mKeyboardView.setVisibility(View.GONE);
- } else {
- mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
- // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
- boolean hardKeyboardVisible = getResources().getConfiguration().hardKeyboardHidden
- == Configuration.HARDKEYBOARDHIDDEN_NO;
- mKeyboardView.setVisibility(
- (ENABLE_HIDE_KEYBOARD && hardKeyboardVisible) ? View.INVISIBLE : View.VISIBLE);
+ mImm = (InputMethodManager) getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
- // 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);
- imeOrDeleteButtonVisible = true;
- pinDelete.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mKeyboardHelper.handleBackspace();
- }
- });
- }
- }
-
- mPasswordEntry.requestFocus();
-
- // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys.
- if (mIsAlpha) {
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- } else {
- mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
- }
+ 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() {
@@ -215,17 +110,17 @@
}
});
+ mPasswordEntry.requestFocus();
+
// If there's more than one IME, enable the IME switcher button
View switchImeButton = findViewById(R.id.switch_ime_button);
- final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (mIsAlpha && switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
+ 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
- imm.showInputMethodPicker();
+ mImm.showInputMethodPicker();
}
});
}
@@ -291,113 +186,6 @@
}
@Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- // send focus to the password field
- return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
- }
-
- private 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(
- mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin, true);
- }
- mPasswordEntry.setText("");
- }
-
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mPasswordEntry.setEnabled(false);
- mKeyboardView.setEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- final AccessibilityManager accessManager =
- (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- // Use a longer update interval when accessibility is turned on.
- final long interval = accessManager.isEnabled() ? ACCESSIBILITY_LOCKOUT_INTERVAL
- : LOCKOUT_INTERVAL;
- new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, interval) {
-
- @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() {
- 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 mIsAlpha;
- }
-
- @Override
- public void onPause() {
-
- }
-
- @Override
- public void onResume() {
- 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) {
- }
-
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
+ public void showUsabilityHint() {
}
}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
index dcf40bf..408a9c8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
@@ -200,6 +200,10 @@
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** TODO: hook this up */
public void cleanUp() {
if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
new file mode 100644
index 0000000..f6a90c5
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
@@ -0,0 +1,20 @@
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+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);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
index 59e2ca9..7a69586 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -31,7 +31,8 @@
Invalid, // NULL state
None, // No security enabled
Pattern, // Unlock by drawing a pattern.
- Password, // Unlock by entering a password or PIN
+ 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.
@@ -85,6 +86,9 @@
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:
@@ -117,7 +121,9 @@
*/
SecurityMode getAlternateFor(SecurityMode mode) {
if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed()
- && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) {
+ && (mode == SecurityMode.Password
+ || mode == SecurityMode.PIN
+ || mode == SecurityMode.Pattern)) {
return SecurityMode.Biometric;
}
return mode; // no alternate, return what was given
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
index 19bcae9..c3684c4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
@@ -62,4 +62,6 @@
KeyguardSecurityCallback getCallback();
void setSecurityMessageDisplay(SecurityMessageDisplay display);
+
+ void showUsabilityHint();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
index c4e1607..9cdbc2d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
@@ -21,14 +21,17 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
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 {
+public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
private Rect mTempRect = new Rect();
public KeyguardSecurityViewFlipper(Context context) {
@@ -55,4 +58,79 @@
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() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onResume();
+ }
+ }
+
+ @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 setSecurityMessageDisplay(SecurityMessageDisplay display) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setSecurityMessageDisplay(display);
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showUsabilityHint();
+ }
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index 1d26def..eba9a76 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -16,18 +16,12 @@
package com.android.internal.policy.impl.keyguard;
import android.animation.ObjectAnimator;
-import android.app.ActivityManagerNative;
import android.app.SearchManager;
import android.app.admin.DevicePolicyManager;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
@@ -41,8 +35,6 @@
import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
import com.android.internal.R;
-import java.util.List;
-
public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
private static final boolean DEBUG = KeyguardHostView.DEBUG;
private static final String TAG = "SecuritySelectorView";
@@ -67,7 +59,7 @@
((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
.getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (assistIntent != null) {
- launchActivity(assistIntent, false);
+ mActivityLauncher.launchActivity(assistIntent, false);
} else {
Log.w(TAG, "Failed to get intent for assist activity");
}
@@ -75,7 +67,7 @@
break;
case com.android.internal.R.drawable.ic_lockscreen_camera:
- launchCamera();
+ mActivityLauncher.launchCamera();
mCallback.userActivity(0);
break;
@@ -119,49 +111,27 @@
}
};
+ 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);
}
- private boolean wouldLaunchResolverActivity(Intent intent) {
- PackageManager packageManager = mContext.getPackageManager();
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
- // 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;
- }
-
- protected void launchCamera() {
- if (mLockPatternUtils.isSecure()) {
- // Launch the secure version of the camera
- final Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- if (wouldLaunchResolverActivity(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(intent, false);
- } else {
- launchActivity(intent, true);
- }
- } else {
- // Launch the normal camera
- launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false);
- }
- }
-
public KeyguardSelectorView(Context context, AttributeSet attrs) {
super(context, attrs);
mLockPatternUtils = new LockPatternUtils(getContext());
@@ -183,7 +153,8 @@
return mGlowPadView.getTargetPosition(resId) != -1;
}
- public void ping() {
+ @Override
+ public void showUsabilityHint() {
mGlowPadView.ping();
}
@@ -266,42 +237,6 @@
mLockPatternUtils = utils;
}
- /**
- * 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}
- */
- private void launchActivity(final Intent intent, boolean showsWhileLocked) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- boolean isSecure = mLockPatternUtils.isSecure();
- if (!isSecure || showsWhileLocked) {
- if (!isSecure) try {
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- Log.w(TAG, "can't dismiss keyguard on launch");
- }
- try {
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- } 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.
- mCallback.setOnDismissRunnable(new Runnable() {
- @Override
- public void run() {
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- }
- });
- mCallback.dismiss(false);
- }
- }
-
@Override
public void reset() {
mGlowPadView.reset(false);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
index 7878e46..018a1aa 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -121,6 +121,10 @@
mPinEntry.requestFocus();
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** {@inheritDoc} */
public void cleanUp() {
// dismiss the dialog.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
index 562d893..d0585b9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
@@ -91,7 +91,8 @@
} else if (state == CONFIRM_PIN) {
if (confirmPin()) {
state = DONE;
- msg = R.string.lockscreen_sim_unlock_progress_dialog_message;
+ msg =
+ com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message;
updateSim();
} else {
msg = R.string.kg_invalid_confirm_pin_hint;
@@ -169,6 +170,10 @@
reset();
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** {@inheritDoc} */
public void cleanUp() {
// dismiss the dialog.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
index 00cd5b9..f2cb522 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -20,6 +20,8 @@
import android.util.AttributeSet;
import android.widget.GridLayout;
+import com.android.internal.widget.LockPatternUtils;
+
public class KeyguardStatusView extends GridLayout {
@SuppressWarnings("unused")
private KeyguardStatusViewManager mStatusViewManager;
@@ -36,6 +38,10 @@
super(context, attrs, defStyle);
}
+ public int getAppWidgetId() {
+ return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index 5b85064..2837a66 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -16,15 +16,6 @@
package com.android.internal.policy.impl.keyguard;
-import com.android.internal.R;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.widget.DigitalClock;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.util.Date;
-
-import libcore.util.MutableInt;
-
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -39,6 +30,10 @@
import android.view.View;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.Date;
import libcore.util.MutableInt;
@@ -78,7 +73,7 @@
// Whether to use the last line as a combined line to either display owner info / charging.
// If false, each item will be given a dedicated space.
private boolean mShareStatusRegion = false;
-
+
// last known battery level
private int mBatteryLevel = 100;
@@ -121,9 +116,9 @@
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
Resources res = getContext().getResources();
- mDateFormatString =
+ mDateFormatString =
res.getText(com.android.internal.R.string.abbrev_wday_month_day_no_year);
- mShareStatusRegion = res.getBoolean(R.bool.kg_share_status_area);
+ mShareStatusRegion = res.getBoolean(com.android.internal.R.bool.kg_share_status_area);
mLockPatternUtils = new LockPatternUtils(view.getContext());
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(view.getContext());
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
deleted file mode 100644
index 1cd796c..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
+++ /dev/null
@@ -1,214 +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.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * A layout that arranges its children into a special type of grid.
- */
-public class KeyguardSubdivisionLayout extends ViewGroup {
- ArrayList<BiTree> mCells = new ArrayList<BiTree>();
- int mNumChildren = -1;
- int mWidth = -1;
- int mHeight = -1;
- int mTopChild = 0;
-
- public KeyguardSubdivisionLayout(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardSubdivisionLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardSubdivisionLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setClipChildren(false);
- setClipToPadding(false);
- setChildrenDrawingOrderEnabled(true);
- }
-
- private class BiTree {
- Rect rect;
- BiTree left;
- BiTree right;
- int nodeDepth;
- ArrayList<BiTree> leafs;
-
- public BiTree(Rect r) {
- rect = r;
- }
-
- public BiTree() {
- }
-
- boolean isLeaf() {
- return (left == null) && (right == null);
- }
-
- int depth() {
- if (left != null && right != null) {
- return Math.max(left.depth(), right.depth()) + 1;
- } else if (left != null) {
- return left.depth() + 1;
- } else if (right != null) {
- return right.depth() + 1;
- } else {
- return 1;
- }
- }
-
- int numLeafs() {
- if (left != null && right != null) {
- return left.numLeafs() + right.numLeafs();
- } else if (left != null) {
- return left.numLeafs();
- } else if (right != null) {
- return right.numLeafs();
- } else {
- return 1;
- }
- }
-
- BiTree getNextNodeToBranch() {
- if (leafs == null) {
- leafs = new ArrayList<BiTree>();
- }
- leafs.clear();
- getLeafs(leafs, 1);
-
- // If the tree is complete, then we start a new level at the rightmost side.
- double r = log2(leafs.size());
- if (Math.ceil(r) == Math.floor(r)) {
- return leafs.get(leafs.size() - 1);
- }
-
- // Tree is not complete, find the first leaf who's depth is less than the depth of
- // the tree.
- int treeDepth = depth();
- for (int i = leafs.size() - 1; i >= 0; i--) {
- BiTree n = leafs.get(i);
- if (n.nodeDepth < treeDepth) {
- return n;
- }
- }
- return null;
- }
-
- // Gets leafs in left to right order
- void getLeafs(ArrayList<BiTree> leafs, int depth) {
- if (isLeaf()) {
- this.nodeDepth = depth;
- leafs.add(this);
- } else {
- if (left != null) {
- left.getLeafs(leafs, depth + 1);
- }
- if (right != null) {
- right.getLeafs(leafs, depth + 1);
- }
- }
- }
- }
-
- double log2(double d) {
- return Math.log(d) / Math.log(2);
- }
-
- private void addCell(BiTree tree) {
- BiTree branch = tree.getNextNodeToBranch();
- Rect r = branch.rect;
- branch.left = new BiTree();
- branch.right = new BiTree();
- int newDepth = tree.depth();
-
- // For each level of the tree, we alternate between horizontal and vertical division
- if (newDepth % 2 == 0) {
- // Divide the cell vertically
- branch.left.rect = new Rect(r.left, r.top, r.right, r.top + r.height() / 2);
- branch.right.rect = new Rect(r.left, r.top + r.height() / 2, r.right, r.bottom);
- } else {
- // Divide the cell horizontally
- branch.left.rect = new Rect(r.left, r.top, r.left + r.width() / 2, r.bottom);
- branch.right.rect = new Rect(r.left + r.width() / 2, r.top, r.right, r.bottom);
- }
- }
-
- private void constructGrid(int width, int height, int numChildren) {
- mCells.clear();
- BiTree root = new BiTree(new Rect(0, 0, width, height));
-
- // We add nodes systematically until the number of leafs matches the number of children
- while (root.numLeafs() < numChildren) {
- addCell(root);
- }
-
- // Spit out the final list of cells
- root.getLeafs(mCells, 1);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- int childCount = getChildCount();
-
- if (mNumChildren != childCount || width != getMeasuredWidth() ||
- height != getMeasuredHeight()) {
- constructGrid(width, height, childCount);
- }
-
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- Rect rect = mCells.get(i).rect;
- child.measure(MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- Rect rect = mCells.get(i).rect;
- child.layout(rect.left, rect.top, rect.right, rect.bottom);
- }
- }
-
- public void setTopChild(int top) {
- mTopChild = top;
- invalidate();
- }
-
- protected int getChildDrawingOrder(int childCount, int i) {
- int ret = i;
- if (i == childCount - 1) {
- ret = mTopChild;
- } else if (i >= mTopChild){
- ret = i + 1;
- }
- return ret;
- }
-}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index 6a3c7c1..89f220a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -16,17 +16,15 @@
package com.android.internal.policy.impl.keyguard;
-import java.lang.ref.WeakReference;
-
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.media.IRemoteControlDisplay;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -42,11 +40,12 @@
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.
*/
@@ -264,7 +263,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+// int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index d8e1c1a..316825a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
private static final int MSG_DPM_STATE_CHANGED = 309;
private static final int MSG_USER_SWITCHED = 310;
private static final int MSG_USER_REMOVED = 311;
+ private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
private static KeyguardUpdateMonitor sInstance;
@@ -147,6 +148,10 @@
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
break;
+ case MSG_KEYGUARD_VISIBILITY_CHANGED:
+ handleKeyguardVisibilityChanged(msg.arg1);
+ break;
+
}
}
};
@@ -557,6 +562,19 @@
}
}
+ /**
+ * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+ */
+ private void handleKeyguardVisibilityChanged(int showing) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardVisibilityChanged(showing == 1);
+ }
+ }
+ }
+
private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
@@ -659,6 +677,13 @@
callback.onSimStateChanged(mSimState);
}
+ 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();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index 3d65e68..8c9ac8b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -62,6 +62,12 @@
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.
*/
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
index 3191f4a..9e3424d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
@@ -16,6 +16,7 @@
package com.android.internal.policy.impl.keyguard;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -27,11 +28,11 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.TelephonyManager;
-import android.view.KeyEvent;
-import android.widget.LinearLayout;
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
@@ -42,7 +43,7 @@
* Handles intercepting of media keys that still work when the keyguard is
* showing.
*/
-public abstract class KeyguardViewBase extends LinearLayout {
+public abstract class KeyguardViewBase extends FrameLayout {
private static final int BACKGROUND_COLOR = 0x70000000;
private AudioManager mAudioManager;
@@ -249,7 +250,10 @@
@Override
public void dispatchSystemUiVisibilityChanged(int visibility) {
super.dispatchSystemUiVisibilityChanged(visibility);
- setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+ }
}
public void setViewMediatorCallback(
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index b66c883..9fa14f5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -48,7 +48,7 @@
* reported to this class by the current {@link KeyguardViewBase}.
*/
public class KeyguardViewManager {
- private final static boolean DEBUG = false;
+ private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
private static String TAG = "KeyguardViewManager";
public static boolean USE_UPPER_CASE = true;
@@ -65,7 +65,6 @@
private FrameLayout mKeyguardHost;
private KeyguardHostView mKeyguardView;
- private boolean mScreenOn = false;
private LockPatternUtils mLockPatternUtils;
public interface ShowListener {
@@ -96,7 +95,7 @@
boolean enableScreenRotation = shouldEnableScreenRotation();
- maybeCreateKeyguardLocked(enableScreenRotation, options);
+ maybeCreateKeyguardLocked(enableScreenRotation, false, options);
maybeEnableScreenRotation(enableScreenRotation);
// Disable common aspects of the system/status/navigation bars that are not appropriate or
@@ -104,7 +103,7 @@
// 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, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
+ if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
mKeyguardHost.setSystemUiVisibility(visFlags);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
@@ -127,7 +126,12 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- maybeCreateKeyguardLocked(shouldEnableScreenRotation(), null);
+ 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
@@ -145,7 +149,8 @@
SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
- private void maybeCreateKeyguardLocked(boolean enableScreenRotation, Bundle options) {
+ private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
+ Bundle options) {
final boolean isActivity = (mContext instanceof Activity); // for test activity
if (mKeyguardHost != null) {
@@ -189,7 +194,9 @@
mViewManager.addView(mKeyguardHost, lp);
}
- inflateKeyguardView(options);
+ if (force || mKeyguardView == null) {
+ inflateKeyguardView(options);
+ }
updateUserActivityTimeoutInWindowLayoutParams();
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
@@ -230,11 +237,6 @@
mKeyguardView.showNextSecurityScreenIfPresent();
}
}
-
- if (mScreenOn) {
- mKeyguardView.show();
- mKeyguardView.requestFocus();
- }
}
public void updateUserActivityTimeout() {
@@ -295,12 +297,11 @@
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(), options);
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
}
public synchronized void onScreenTurnedOff() {
if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
- mScreenOn = false;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOff();
}
@@ -309,7 +310,6 @@
public synchronized void onScreenTurnedOn(
final KeyguardViewManager.ShowListener showListener) {
if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
- mScreenOn = true;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOn();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index cb70922..bc12e96 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,6 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import com.android.internal.telephony.IccCardConstants;
@@ -96,7 +95,7 @@
*/
public class KeyguardViewMediator {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
- private final static boolean DEBUG = false;
+ final static boolean DEBUG = false;
private final static boolean DBG_WAKE = false;
private final static String TAG = "KeyguardViewMediator";
@@ -520,11 +519,11 @@
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
mUpdateMonitor.registerCallback(mUpdateCallback);
-
+
// Disable alternate unlock right after boot until things have settled.
mUpdateMonitor.setAlternateUnlockEnabled(false);
mUpdateMonitor.setIsFirstBoot(true);
-
+
doKeyguardLocked();
}
// Most services aren't available until the system reaches the ready state, so we
@@ -629,7 +628,9 @@
mScreenOn = true;
cancelDoKeyguardLaterLocked();
if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
- notifyScreenOnLocked(showListener);
+ if (showListener != null) {
+ notifyScreenOnLocked(showListener);
+ }
}
maybeSendUserPresentBroadcast();
}
@@ -769,6 +770,7 @@
*/
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);
@@ -1306,13 +1308,6 @@
// (like recents). Temporary enable/disable (e.g. the "back" button) are
// done in KeyguardHostView.
flags |= StatusBarManager.DISABLE_RECENT;
- if (!mScreenOn) {
- // Disable all navbar buttons on screen off. The navigation bar will hide
- // these immediately to avoid seeing the end of layout transition animations
- // if quickly turning back on.
- flags |= StatusBarManager.DISABLE_HOME;
- flags |= StatusBarManager.DISABLE_BACK;
- }
if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
// showing secure lockscreen; disable expanding.
flags |= StatusBarManager.DISABLE_EXPAND;
@@ -1328,7 +1323,9 @@
+ " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
}
- mStatusBarManager.disable(flags);
+ if (!(mContext instanceof Activity)) {
+ mStatusBarManager.disable(flags);
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
new file mode 100644
index 0000000..c163b97
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -0,0 +1,168 @@
+/*
+ * 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.util.Log;
+import android.view.View;
+
+public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChallengeScrolledListener {
+
+ private KeyguardWidgetPager mPagedView;
+ private int mCurrentPageIndex;
+ private ChallengeLayout mChallengeLayout;
+ private Runnable mHideHintsRunnable;
+ private KeyguardSecurityView mKeyguardSecurityContainer;
+ private int[] mTmpPoint = new int[2];
+ private static final int SCREEN_ON_HINT_DURATION = 1000;
+ Handler mMainQueue = new Handler(Looper.myLooper());
+
+ int mChallengeTop = 0;
+
+ public KeyguardViewStateManager() {
+ }
+
+ public void setPagedView(KeyguardWidgetPager pagedView) {
+ mPagedView = pagedView;
+ }
+
+ public void setChallengeLayout(ChallengeLayout layout) {
+ mChallengeLayout = layout;
+ }
+
+ public void setSecurityViewContainer(KeyguardSecurityView container) {
+ mKeyguardSecurityContainer = container;
+ }
+
+ public void onPageBeginMoving() {
+ if (mChallengeLayout.isChallengeShowing()) {
+ mChallengeLayout.showChallenge(false);
+ }
+ if (mHideHintsRunnable != null) {
+ mMainQueue.removeCallbacks(mHideHintsRunnable);
+ mHideHintsRunnable = null;
+ }
+ }
+
+ public void onPageEndMoving() {
+ }
+
+ public void showBouncer(boolean show) {
+ mChallengeLayout.showBouncer();
+ }
+
+ public void onPageSwitch(View newPage, int newPageIndex) {
+ // Reset the previous page size and ensure the current page is sized appropriately
+ if (mPagedView != null) {
+ KeyguardWidgetFrame oldPage = mPagedView.getWidgetPageAt(mCurrentPageIndex);
+ // Reset the old widget page to full size
+ if (oldPage != null) {
+ oldPage.resetSize();
+ }
+
+ KeyguardWidgetFrame newCurPage = mPagedView.getWidgetPageAt(newPageIndex);
+ if (mChallengeLayout.isChallengeOverlapping()) {
+ sizeWidgetFrameToChallengeTop(newCurPage);
+ }
+ }
+ mCurrentPageIndex = newPageIndex;
+ }
+
+ private void sizeWidgetFrameToChallengeTop(KeyguardWidgetFrame frame) {
+ if (frame == null) return;
+ mTmpPoint[0] = 0;
+ mTmpPoint[1] = mChallengeTop;
+ mapPoint((View) mChallengeLayout, frame, mTmpPoint);
+ frame.setChallengeTop(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
+ */
+ public void mapPoint(View fromView, View toView, int pt[]) {
+ int[] loc = new int[2];
+ fromView.getLocationInWindow(loc);
+ int x = loc[0];
+ int y = loc[1];
+
+ toView.getLocationInWindow(loc);
+ int vX = loc[0];
+ int vY = loc[1];
+
+ pt[0] += x - vX;
+ pt[1] += y - vY;
+ }
+
+ @Override
+ public void onScrollStateChanged(int scrollState) {
+ if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+ if (mPagedView == null) return;
+
+ boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+ int curPage = mPagedView.getCurrentPage();
+ KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(curPage);
+
+ if (frame != null) {
+ if (!challengeOverlapping) {
+ frame.resetSize();
+ } else {
+ sizeWidgetFrameToChallengeTop(frame);
+ }
+ }
+
+ if (challengeOverlapping) {
+ mPagedView.setOnlyAllowEdgeSwipes(true);
+ } else {
+ mPagedView.setOnlyAllowEdgeSwipes(false);
+ }
+
+ if (mChallengeLayout.isChallengeShowing()) {
+ mKeyguardSecurityContainer.onResume();
+ } else {
+ mKeyguardSecurityContainer.onPause();
+ }
+ } else {
+ // View is on the move. Pause the security view until it completes.
+ mKeyguardSecurityContainer.onPause();
+ }
+ }
+
+ public void showUsabilityHints() {
+ mKeyguardSecurityContainer.showUsabilityHint();
+ mPagedView.showInitialPageHints();
+ mHideHintsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mPagedView.hideOutlinesAndSidePages();
+ mHideHintsRunnable = null;
+ }
+ };
+
+ mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
+ }
+
+ @Override
+ public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
+ mChallengeTop = challengeTop;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
new file mode 100644
index 0000000..02c32d4
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.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.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
+
+ private float mAdjacentPagesAngle;
+ private static float CAMERA_DISTANCE = 10000;
+
+ 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 1.5f;
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+ if (!isInOverscroll) {
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (child != null) {
+ float scrollProgress = getScrollProgress(screenCenter, child, i);
+ if (!isReordering(false)) {
+ child.setBackgroundAlphaMultiplier(
+ backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+ } else {
+ child.setBackgroundAlphaMultiplier(1f);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void screenScrolled(int 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);
+ int width = v.getMeasuredWidth();
+ float pivotX = (width / 2f) + scrollProgress * (width / 2f);
+ float pivotY = v.getMeasuredHeight() / 2;
+ float rotationY = - mAdjacentPagesAngle * scrollProgress;
+ v.setCameraDistance(CAMERA_DISTANCE);
+ v.setPivotX(pivotX);
+ v.setPivotY(pivotY);
+ v.setRotationY(rotationY);
+ }
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index 311eec6..9e1189c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -16,20 +16,20 @@
package com.android.internal.policy.impl.keyguard;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Color;
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.os.PowerManager;
-import android.os.SystemClock;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
import android.widget.FrameLayout;
import com.android.internal.R;
@@ -48,8 +48,12 @@
private float mOverScrollAmount = 0f;
private final Rect mForegroundRect = new Rect();
private int mForegroundAlpha = 0;
- private PowerManager mPowerManager;
- private boolean mDisableInteraction;
+ private CheckLongPressHelper mLongPressHelper;
+
+ private float mBackgroundAlpha;
+ private float mBackgroundAlphaMultiplier = 1.0f;
+ private Drawable mBackgroundDrawable;
+ private Rect mBackgroundRect = new Rect();
public KeyguardWidgetFrame(Context context) {
this(context, null, 0);
@@ -62,35 +66,115 @@
public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mLongPressHelper = new CheckLongPressHelper(this);
Resources res = context.getResources();
- int hPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_horizontal_padding);
- int topPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_top_padding);
- int bottomPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
- setPadding(hPadding, topPadding, hPadding, bottomPadding);
+ // TODO: this padding should really correspond to the padding embedded in the background
+ // drawable (ie. outlines).
+ int padding = (int) (res.getDisplayMetrics().density * 8);
+ setPadding(padding, padding, padding, padding);
+
+ mBackgroundDrawable = res.getDrawable(R.drawable.security_frame);
mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient);
mGradientPaint.setXfermode(sAddBlendMode);
}
- public void setDisableUserInteraction(boolean disabled) {
- mDisableInteraction = disabled;
+ @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 onInterceptTouchEvent(MotionEvent ev) {
- if (!mDisableInteraction) {
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
- return super.onInterceptTouchEvent(ev);
+ 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();
+ }
+
+ @Override
protected void dispatchDraw(Canvas canvas) {
+ drawBg(canvas);
super.dispatchDraw(canvas);
drawGradientOverlay(canvas);
+ }
+ /**
+ * 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 View getContent() {
+ return getChildAt(0);
+ }
+
+ public int getContentAppWidgetId() {
+ View content = getContent();
+ if (content instanceof AppWidgetHostView) {
+ return ((AppWidgetHostView) content).getAppWidgetId();
+ } else {
+ return ((KeyguardStatusView) content).getAppWidgetId();
+ }
}
private void drawGradientOverlay(Canvas c) {
@@ -99,6 +183,81 @@
c.drawRect(mForegroundRect, mGradientPaint);
}
+ protected void drawBg(Canvas canvas) {
+ if (mBackgroundAlpha > 0.0f) {
+ Drawable bg = mBackgroundDrawable;
+
+ bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
+ bg.setBounds(mBackgroundRect);
+ bg.draw(canvas);
+ }
+ }
+
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
+ public void setBackgroundAlphaMultiplier(float multiplier) {
+ if (mBackgroundAlphaMultiplier != multiplier) {
+ mBackgroundAlphaMultiplier = multiplier;
+ invalidate();
+ }
+ }
+
+ public float getBackgroundAlphaMultiplier() {
+ return mBackgroundAlphaMultiplier;
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ if (mBackgroundAlpha != alpha) {
+ mBackgroundAlpha = alpha;
+ invalidate();
+ }
+ }
+
+ public void setContentAlpha(float 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
+ */
+ public 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();
+ }
+ }
+
+ /**
+ * Set the top location of the challenge.
+ *
+ * @param top The top of the challenge, in _local_ coordinates, or -1 to indicate the challenge
+ * is down.
+ */
+ public void setChallengeTop(int top) {
+ // The widget starts below the padding, and extends to the top of the challengs.
+ int widgetHeight = top - getPaddingTop();
+ setWidgetHeight(widgetHeight);
+ }
+
+ public void resetSize() {
+ setWidgetHeight(LayoutParams.MATCH_PARENT);
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -110,6 +269,7 @@
mGradientColor, 0, Shader.TileMode.CLAMP);
mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
mGradientColor, 0, Shader.TileMode.CLAMP);
+ mBackgroundRect.set(0, 0, w, h);
}
void setOverScrollAmount(float r, boolean left) {
@@ -120,4 +280,8 @@
invalidate();
}
}
+
+ public void onActive(boolean isActive) {
+ // hook for subclasses
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 1e65665..63e7fdd 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -15,29 +15,52 @@
*/
package com.android.internal.policy.impl.keyguard;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
-
import android.widget.FrameLayout;
import com.android.internal.R;
-public class KeyguardWidgetPager extends PagedView {
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
+ OnLongClickListener {
+
ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
private static float CAMERA_DISTANCE = 10000;
- private static float TRANSITION_SCALE_FACTOR = 0.74f;
- private static float TRANSITION_PIVOT = 0.65f;
private static float TRANSITION_MAX_ROTATION = 30;
private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
- private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
- private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+ private KeyguardViewStateManager mViewStateManager;
+ private LockPatternUtils mLockPatternUtils;
+
+ // Related to the fading in / out background outlines
+ private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
+ private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
+ private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
+ private ObjectAnimator mChildrenOutlineFadeInAnimation;
+ private ObjectAnimator mChildrenOutlineFadeOutAnimation;
+ private float mChildrenOutlineAlpha = 0;
+ private float mSidePagesAlpha = 1f;
+
+ private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
+
+ private int mPage = 0;
+ private Callbacks mCallbacks;
+
+ private boolean mCameraWidgetEnabled;
public KeyguardWidgetPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -52,28 +75,180 @@
if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+
+ setPageSwitchListener(this);
+
+ Resources r = getResources();
+ mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget);
+ }
+
+ public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
+ mViewStateManager = viewStateManager;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils l) {
+ mLockPatternUtils = l;
+ }
+
+ @Override
+ public void onPageSwitch(View newPage, int newPageIndex) {
+ boolean showingStatusWidget = false;
+ if (newPage instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) newPage;
+ if (vg.getChildAt(0) instanceof KeyguardStatusView) {
+ showingStatusWidget = true;
+ }
+ }
+
+ // Disable the status bar clock if we're showing the default status widget
+ if (showingStatusWidget) {
+ 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;
+ if (mCallbacks != null) {
+ mCallbacks.onUserActivityTimeoutChanged();
+ mCallbacks.userActivity();
+ mCallbacks.onPageSwitch(newPageIndex);
+ }
+ KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
+ if (oldWidgetPage != null) {
+ oldWidgetPage.onActive(false);
+ }
+ KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
+ if (newWidgetPage != null) {
+ newWidgetPage.onActive(true);
+ }
+ }
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageSwitch(newPage, newPageIndex);
+ }
+ }
+
+ 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 onPageSwitch(int newPageIndex);
+ }
+
+ public void addWidget(View widget) {
+ addWidget(widget, -1);
+ }
+
+
+ public void onRemoveView(View v) {
+ int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ mLockPatternUtils.removeAppWidget(appWidgetId);
+ }
+
+ public void onAddView(View v, int index) {
+ int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ // Subtract from the index to take into account pages before the reorderable
+ // pages (e.g. the "add widget" page)
+ mLockPatternUtils.addAppWidget(appWidgetId, index - mTempVisiblePagesRange[0]);
}
/*
- * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+ * We wrap widgets in a special frame which handles drawing the over scroll foreground.
*/
- public void addWidget(AppWidgetHostView widget) {
- KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
- lp.gravity = Gravity.CENTER;
- // 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);
- widget.setContentDescription(widget.getAppWidgetInfo().label);
- frame.addView(widget, lp);
- addView(frame);
+ 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);
+ if (widget instanceof AppWidgetHostView) {
+ AppWidgetHostView awhv = (AppWidgetHostView) widget;
+ widget.setContentDescription(awhv.getAppWidgetInfo().label);
+ }
+ frame.addView(widget, lp);
+ } else {
+ frame = (KeyguardWidgetFrame) widget;
+ }
+
+ ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ frame.setOnLongClickListener(this);
+
+ if (pageIndex == -1) {
+ addView(frame, pageLp);
+ } else {
+ addView(frame, pageIndex, pageLp);
+ }
+ }
+
+ // We enforce that all children are KeyguardWidgetFrames
+ @Override
+ public void addView(View child, int index) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, index);
+ }
+
+ @Override
+ public void addView(View child, int width, int height) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, width, height);
+ }
+
+ @Override
+ public void addView(View child, LayoutParams params) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, params);
+ }
+
+ @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) {
- if (getParent() instanceof KeyguardWidgetRegion) {
- ((KeyguardWidgetRegion) getParent()).showPagingFeedback();
- }
+ showPagingFeedback();
}
@Override
@@ -82,8 +257,13 @@
// TODO: We should only do this for the two views that are actually moving
int children = getChildCount();
for (int i = 0; i < children; i++) {
- getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null);
+ getWidgetPageAt(i).enableHardwareLayersForContent();
}
+
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageBeginMoving();
+ }
+ showOutlinesAndSidePages();
}
@Override
@@ -91,8 +271,13 @@
// Disable hardware layers while pages are moving
int children = getChildCount();
for (int i = 0; i < children; i++) {
- getChildAt(i).setLayerType(LAYER_TYPE_NONE, null);
+ getWidgetPageAt(i).disableHardwareLayersForContent();
}
+
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageEndMoving();
+ }
+ hideOutlinesAndSidePages();
}
/*
@@ -118,7 +303,7 @@
public String getCurrentPageDescription() {
final int nextPageIndex = getNextPage();
if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) {
- KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex);
+ KeyguardWidgetFrame frame = getWidgetPageAt(nextPageIndex);
CharSequence title = frame.getChildAt(0).getContentDescription();
if (title == null) {
title = "";
@@ -135,74 +320,59 @@
acceleratedOverScroll(amount);
}
- // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
+ float backgroundAlphaInterpolator(float r) {
+ return Math.min(1f, r);
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+ if (!isInOverscroll) {
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (child != null) {
+ float scrollProgress = getScrollProgress(screenCenter, child, i);
+ if (!isReordering(false)) {
+ child.setBackgroundAlphaMultiplier(
+ backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+ } else {
+ child.setBackgroundAlphaMultiplier(1f);
+ }
+ }
+ }
+ }
+ }
+
@Override
protected void screenScrolled(int screenCenter) {
- super.screenScrolled(screenCenter);
-
+ updatePageAlphaValues(screenCenter);
for (int i = 0; i < getChildCount(); i++) {
- View v = getPageAt(i);
+ KeyguardWidgetFrame v = getWidgetPageAt(i);
+ if (v == mDragView) continue;
if (v != null) {
float scrollProgress = getScrollProgress(screenCenter, v, i);
- float interpolatedProgress =
- mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
- float scale = (1 - interpolatedProgress) +
- interpolatedProgress * TRANSITION_SCALE_FACTOR;
- float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
-
- float alpha;
-
- if (scrollProgress < 0) {
- alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
- 1 - Math.abs(scrollProgress)) : 1.0f;
- } else {
- // On large screens we need to fade the page as it nears its leftmost position
- alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
- }
+ float alpha = 1.0f;
v.setCameraDistance(mDensity * CAMERA_DISTANCE);
- int pageWidth = v.getMeasuredWidth();
- int pageHeight = v.getMeasuredHeight();
if (PERFORM_OVERSCROLL_ROTATION) {
if (i == 0 && scrollProgress < 0) {
- // Overscroll to the left
- v.setPivotX(TRANSITION_PIVOT * pageWidth);
+ // Over scroll to the left
v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
- true);
- }
- scale = 1.0f;
+ v.setOverScrollAmount(Math.abs(scrollProgress), true);
alpha = 1.0f;
// On the first page, we don't want the page to have any lateral motion
- translationX = 0;
} else if (i == getChildCount() - 1 && scrollProgress > 0) {
- // Overscroll to the right
- v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
+ // Over scroll to the right
v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- scale = 1.0f;
alpha = 1.0f;
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
- false);
- }
+ v.setOverScrollAmount(Math.abs(scrollProgress), false);
// On the last page, we don't want the page to have any lateral motion.
- translationX = 0;
} else {
- v.setPivotY(pageHeight / 2.0f);
- v.setPivotX(pageWidth / 2.0f);
v.setRotationY(0f);
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
- }
+ v.setOverScrollAmount(0, false);
}
}
-
- v.setTranslationX(translationX);
- v.setScaleX(scale);
- v.setScaleY(scale);
v.setAlpha(alpha);
// If the view has 0 alpha, we set it to be invisible so as to prevent
@@ -215,4 +385,130 @@
}
}
}
+ @Override
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ if (isReordering) {
+ if (isAddWidgetPageVisible()) {
+ range[0]++;
+ }
+ if (isMusicWidgetVisible()) {
+ range[1]--;
+ }
+ if (isCameraWidgetVisible()) {
+ range[1]--;
+ }
+ }
+ }
+
+ /*
+ * Special widgets
+ */
+ boolean isAddWidgetPageVisible() {
+ // TODO: Make proper test once we decide whether the add-page is always showing
+ return true;
+ }
+ boolean isMusicWidgetVisible() {
+ // TODO: Make proper test once we have music in the list
+ return false;
+ }
+ boolean isCameraWidgetVisible() {
+ return mCameraWidgetEnabled;
+ }
+
+ @Override
+ protected void onStartReordering() {
+ super.onStartReordering();
+ setChildrenOutlineMultiplier(1.0f);
+ showOutlinesAndSidePages();
+ }
+
+ @Override
+ protected void onEndReordering() {
+ super.onEndReordering();
+ hideOutlinesAndSidePages();
+ }
+
+ void showOutlinesAndSidePages() {
+ if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
+ if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
+
+ PropertyValuesHolder outlinesAlpha =
+ PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 1.0f);
+ PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 1.0f);
+ mChildrenOutlineFadeInAnimation =
+ ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha);
+
+ mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
+ mChildrenOutlineFadeInAnimation.start();
+ }
+
+ public void showInitialPageHints() {
+ // We start with everything showing
+ setChildrenOutlineAlpha(1.0f);
+ setSidePagesAlpha(1.0f);
+ setChildrenOutlineMultiplier(1.0f);
+
+ int currPage = getCurrentPage();
+ KeyguardWidgetFrame frame = getWidgetPageAt(currPage);
+ frame.setBackgroundAlphaMultiplier(0f);
+ }
+
+ void hideOutlinesAndSidePages() {
+ if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
+ if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
+
+ PropertyValuesHolder outlinesAlpha =
+ PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 0f);
+ PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 0f);
+ mChildrenOutlineFadeOutAnimation =
+ ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha);
+
+ mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
+ mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
+ mChildrenOutlineFadeOutAnimation.start();
+ }
+
+ public void setChildrenOutlineAlpha(float alpha) {
+ mChildrenOutlineAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ getWidgetPageAt(i).setBackgroundAlpha(alpha);
+ }
+ }
+
+ public void setSidePagesAlpha(float alpha) {
+ // This gives the current page, or the destination page if in transit.
+ int curPage = getNextPage();
+ mSidePagesAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ if (curPage != i) {
+ getWidgetPageAt(i).setContentAlpha(alpha);
+ } else {
+ // We lock the current page alpha to 1.
+ getWidgetPageAt(i).setContentAlpha(1.0f);
+ }
+ }
+ }
+
+ public void setChildrenOutlineMultiplier(float alpha) {
+ mChildrenOutlineAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ getWidgetPageAt(i).setBackgroundAlphaMultiplier(alpha);
+ }
+ }
+
+ public float getSidePagesAlpha() {
+ return mSidePagesAlpha;
+ }
+
+ public float getChildrenOutlineAlpha() {
+ return mChildrenOutlineAlpha;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (startReordering()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
deleted file mode 100644
index 4ff6f27..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
+++ /dev/null
@@ -1,125 +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.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-public class KeyguardWidgetRegion extends LinearLayout implements PagedView.PageSwitchListener {
- KeyguardGlowStripView mLeftStrip;
- KeyguardGlowStripView mRightStrip;
- KeyguardWidgetPager mPager;
- private int mPage = 0;
- private Callbacks mCallbacks;
-
- // We are disabling touch interaction of the widget region for factory ROM.
- private static final boolean DISABLE_TOUCH_INTERACTION = true;
-
- private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
-
- public KeyguardWidgetRegion(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardWidgetRegion(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetRegion(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLeftStrip = (KeyguardGlowStripView) findViewById(R.id.left_strip);
- mRightStrip = (KeyguardGlowStripView) findViewById(R.id.right_strip);
- mPager = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
- mPager.setPageSwitchListener(this);
-
- setSoundEffectsEnabled(false);
- if (!DISABLE_TOUCH_INTERACTION) {
- setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- showPagingFeedback();
- }
- });
- }
- }
-
- public void showPagingFeedback() {
- if ((mPage < mPager.getPageCount() - 1)) {
- mLeftStrip.makeEmGo();
- }
- if ((mPage > 0)) {
- mRightStrip.makeEmGo();
- }
- }
-
- @Override
- public void onPageSwitch(View newPage, int newPageIndex) {
- boolean showingStatusWidget = false;
- if (newPage instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) newPage;
- if (vg.getChildAt(0) instanceof KeyguardStatusView) {
- showingStatusWidget = true;
- }
- }
-
- // Disable the status bar clock if we're showing the default status widget
- if (showingStatusWidget) {
- 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) {
- mPage = newPageIndex;
- if (mCallbacks != null) {
- mCallbacks.onUserActivityTimeoutChanged();
- mCallbacks.userActivity();
- }
- }
- }
-
- public long getUserActivityTimeout() {
- View page = mPager.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();
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
new file mode 100644
index 0000000..3ccc7ea
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -0,0 +1,491 @@
+/*
+ * 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.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+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;
+
+ private View mChallengeView;
+ private View mUserSwitcherView;
+ private View mScrimView;
+ private OnBouncerStateChangedListener mBouncerListener;
+
+ private final Rect mTempRect = new Rect();
+
+ 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();
+ }
+
+ @Override
+ public boolean isChallengeShowing() {
+ return true;
+ }
+
+ @Override
+ public boolean isChallengeOverlapping() {
+ return false;
+ }
+
+ @Override
+ public void showChallenge(boolean b) {
+ }
+
+ @Override
+ public void showBouncer() {
+ if (mIsBouncing) return;
+ mIsBouncing = true;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ mIsBouncing = false;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ 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.setVisibility(mIsBouncing ? VISIBLE : GONE);
+ mScrimView.setFocusable(true);
+ mScrimView.setOnClickListener(mScrimClickListener);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, 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");
+ }
+ mChallengeView = 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, MeasureSpec.getSize(widthSpec)),
+ MeasureSpec.EXACTLY);
+ }
+ if (lp.maxHeight >= 0) {
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxHeight, MeasureSpec.getSize(heightSpec)),
+ 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;
+ }
+
+ 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(
+ MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ (int) ((height - heightUsed) * lp.centerWithinArea + 0.5f),
+ MeasureSpec.EXACTLY);
+ }
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(heightSpec) - heightUsed, 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);
+
+ // 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;
+ }
+
+ 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 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 - padding.top - padding.bottom;
+ 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
+ ? height - padding.bottom - (adjustedHeight - childHeight) / 2
+ : height - padding.bottom;
+ top = bottom - childHeight;
+ if (adjustPadding && isVertical) {
+ padding.bottom = height - top;
+ padding.top += childHeight / 2;
+ }
+ break;
+ case Gravity.CENTER_VERTICAL:
+ final int paddedHeight = height - padding.top - padding.bottom;
+ top = padding.top + (paddedHeight - 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 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
new file mode 100644
index 0000000..060cc03
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
@@ -0,0 +1,116 @@
+/*
+ * 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;
+ }
+ }
+ }
+ if (mTextView != null) {
+ 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);
+
+ 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(extra);
+ builder.setSpan(
+ new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
+ builder.length()-extraLen, builder.length(), 0);
+ }
+ }
+ }
+ setText(builder);
+ }
+
+ 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/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 86c05b1..3562071 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -18,12 +18,16 @@
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.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
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;
@@ -41,6 +45,8 @@
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.widget.Scroller;
@@ -52,7 +58,7 @@
* An abstraction of the original Workspace which supports browsing through a
* sequential list of "pages"
*/
-public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+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;
@@ -60,7 +66,7 @@
// 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 = 550;
+ 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;
@@ -78,7 +84,9 @@
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 = true;
+ 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;
@@ -100,7 +108,11 @@
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;
@@ -114,6 +126,8 @@
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;
@@ -121,22 +135,13 @@
protected OnLongClickListener mLongClickListener;
- protected boolean mAllowLongPress = true;
-
protected int mTouchSlop;
private int mPagingTouchSlop;
private int mMaximumVelocity;
private int mMinimumWidth;
protected int mPageSpacing;
- protected int mPageLayoutPaddingTop;
- protected int mPageLayoutPaddingBottom;
- protected int mPageLayoutPaddingLeft;
- protected int mPageLayoutPaddingRight;
- protected int mPageLayoutWidthGap;
- protected int mPageLayoutHeightGap;
protected int mCellCountX = 0;
protected int mCellCountY = 0;
- protected boolean mCenterPagesVertically;
protected boolean mAllowOverScroll = true;
protected int mUnboundedScrollX;
protected int[] mTempVisiblePagesRange = new int[2];
@@ -162,7 +167,7 @@
protected boolean mContentIsRefreshable = true;
// If true, modify alpha of neighboring pages as user scrolls left/right
- protected boolean mFadeInAdjacentScreens = true;
+ protected boolean mFadeInAdjacentScreens = false;
// It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
// to switch to a new page
@@ -188,6 +193,47 @@
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;
+ private int REORDERING_REORDER_REPOSITION_DURATION = 350;
+ private int REORDERING_ZOOM_IN_OUT_DURATION = 250;
+ private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 500;
+ private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
+ private float mMinScale = 1f;
+ protected View mDragView;
+ private 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;
+
+ // 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];
+
+ // 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 = 35f;
+ private int FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
+ protected int mFlingToDeleteThresholdVelocity = -1400;
+ private boolean mIsFlingingToDelete = false;
+
public interface PageSwitchListener {
void onPageSwitch(View newPage, int newPageIndex);
}
@@ -205,24 +251,15 @@
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
- mPageLayoutPaddingTop = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingTop, 0);
- mPageLayoutPaddingBottom = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingBottom, 0);
- mPageLayoutPaddingLeft = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingLeft, 0);
- mPageLayoutPaddingRight = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingRight, 0);
- mPageLayoutWidthGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutWidthGap, 0);
- mPageLayoutHeightGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutHeightGap, 0);
mScrollIndicatorPaddingLeft =
a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
mScrollIndicatorPaddingRight =
- a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
a.recycle();
+ mEdgeSwipeRegionSize =
+ getResources().getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
+
setHapticFeedbackEnabled(false);
init();
}
@@ -235,7 +272,6 @@
mDirtyPageContent.ensureCapacity(32);
mScroller = new Scroller(getContext(), new ScrollInterpolator());
mCurrentPage = 0;
- mCenterPagesVertically = true;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
@@ -243,12 +279,76 @@
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);
}
+ // Convenience methods to map points from self to parent and vice versa
+ float[] mapPointFromSelfToParent(float x, float y) {
+ mTmpPoint[0] = x;
+ mTmpPoint[1] = y;
+ getMatrix().mapPoints(mTmpPoint);
+ mTmpPoint[0] += getLeft();
+ mTmpPoint[1] += getTop();
+ return mTmpPoint;
+ }
+ float[] mapPointFromParentToSelf(float x, float y) {
+ mTmpPoint[0] = x - getLeft();
+ mTmpPoint[1] = y - getTop();
+ 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 = mapPointFromParentToSelf(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) {
@@ -328,6 +428,10 @@
invalidate();
}
+ public void setOnlyAllowEdgeSwipes(boolean enable) {
+ mOnlyAllowEdgeSwipes = enable;
+ }
+
protected void notifyPageSwitchListener() {
if (mPageSwitchListener != null) {
mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
@@ -400,6 +504,14 @@
mTouchX = x;
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+ // Update the last motion events when scrolling
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
}
// we moved this functionality to a helper function so SmoothPagedView can reuse it
@@ -454,10 +566,20 @@
return;
}
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ // 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
+ int parentWidthSize = (int) (1.5f * parent.getMeasuredWidth());
+ int parentHeightSize = parent.getMeasuredHeight();
+ 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);
@@ -481,13 +603,29 @@
// 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 = MeasureSpec.EXACTLY;
- int childHeightMode = MeasureSpec.EXACTLY;
+ 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);
@@ -496,21 +634,20 @@
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
-
- setMeasuredDimension(widthSize, heightSize);
+ 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()) {
+ if (mChildCountOnLastMeasure != getChildCount() && !mIsFlingingToDelete) {
setCurrentPage(mCurrentPage);
}
mChildCountOnLastMeasure = getChildCount();
if (childCount > 0) {
- if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
+ getChildWidth(0));
// Calculate the variable page spacing if necessary
@@ -547,19 +684,21 @@
}
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
- final int verticalPadding = getPaddingTop() + getPaddingBottom();
final int childCount = getChildCount();
- int childLeft = getRelativeChildOffset(0);
+ 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();
- int childTop = getPaddingTop();
- if (mCenterPagesVertically) {
- childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
- }
if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
child.layout(childLeft, childTop,
@@ -577,22 +716,6 @@
}
protected void screenScrolled(int screenCenter) {
- if (isScrollingIndicatorEnabled()) {
- updateScrollingIndicator();
- }
- boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
-
- if (mFadeInAdjacentScreens && !isInOverscroll) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != null) {
- float scrollProgress = getScrollProgress(screenCenter, child, i);
- float alpha = 1 - Math.abs(scrollProgress);
- child.setAlpha(alpha);
- }
- }
- invalidate();
- }
}
@Override
@@ -606,7 +729,7 @@
@Override
public void onChildViewRemoved(View parent, View child) {
- // TODO Auto-generated method stub
+ mForceScreenScrolled = true;
}
protected void invalidateCachedOffsets() {
@@ -659,7 +782,7 @@
} else {
final int padding = getPaddingLeft() + getPaddingRight();
final int offset = getPaddingLeft() +
- (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
+ (getViewportWidth() - padding - getChildWidth(index)) / 2;
if (mChildRelativeOffsets != null) {
mChildRelativeOffsets[index] = offset;
}
@@ -676,33 +799,47 @@
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 = getMeasuredWidth();
+ 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() < getScrollX()) {
+ currPage.getPaddingRight() < offsetX) {
leftScreen++;
currPage = getPageAt(leftScreen);
}
rightScreen = leftScreen;
currPage = getPageAt(rightScreen + 1);
while (rightScreen < pageCount - 1 &&
- currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
+ currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
rightScreen++;
currPage = getPageAt(rightScreen + 1);
}
- range[0] = leftScreen;
- range[1] = rightScreen;
+
+ // 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) {
@@ -711,7 +848,7 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- int halfScreenSize = getMeasuredWidth() / 2;
+ 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;
@@ -728,6 +865,7 @@
final int pageCount = getChildCount();
if (pageCount > 0) {
getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(isReordering(false), mTempVisiblePagesRange);
final int leftScreen = mTempVisiblePagesRange[0];
final int rightScreen = mTempVisiblePagesRange[1];
if (leftScreen != -1 && rightScreen != -1) {
@@ -737,13 +875,20 @@
canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
getScrollY() + getBottom() - getTop());
- for (int i = getChildCount() - 1; i >= 0; i--) {
+ // 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();
}
@@ -836,31 +981,17 @@
}
/**
- * {@inheritDoc}
- */
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- if (disallowIntercept) {
- // We need to make sure to cancel our long press if
- // a scrollable widget takes over touch events
- final View currentPage = getPageAt(mCurrentPage);
- currentPage.cancelLongPress();
- }
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
- /**
* 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 < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+ 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 > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+ return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
}
@Override
@@ -912,12 +1043,23 @@
final float y = ev.getY();
// Remember location of down touch
mDownMotionX = x;
+ mDownMotionY = y;
+ mDownScrollX = getScrollX();
mLastMotionX = x;
mLastMotionY = y;
+ float[] p = mapPointFromSelfToParent(x, y);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
- mAllowLongPress = true;
+
+ // 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;
@@ -935,12 +1077,14 @@
// check if this can be the beginning of a tap on the side of the pages
// to scroll the current page
- 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;
+ 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;
+ }
}
}
}
@@ -949,10 +1093,7 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST;
- mAllowLongPress = false;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -982,9 +1123,17 @@
* of the down event.
*/
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+
if (pointerIndex == -1) {
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 float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
@@ -1002,38 +1151,27 @@
mTotalMotionX += Math.abs(mLastMotionX - x);
mLastMotionX = x;
mLastMotionXRemainder = 0;
- mTouchX = getScrollX();
+ mTouchX = getViewportOffsetX() + getScrollX();
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
pageBeginMoving();
}
- // Either way, cancel any pending longpress
- cancelCurrentPageLongPress();
}
}
- protected void cancelCurrentPageLongPress() {
- if (mAllowLongPress) {
- mAllowLongPress = false;
- // Try canceling the long press. It could also have been scheduled
- // by a distant descendant, so use the mAllowLongPress flag to block
- // everything
- final View currentPage = getPageAt(mCurrentPage);
- if (currentPage != null) {
- currentPage.cancelLongPress();
- }
- }
+ protected float getMaxScrollProgress() {
+ return 1.0f;
}
protected float getScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getMeasuredWidth() / 2;
+ 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, 1.0f);
- scrollProgress = Math.max(scrollProgress, -1.0f);
+ scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
+ scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
return scrollProgress;
}
@@ -1045,7 +1183,7 @@
}
protected void acceleratedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
// We want to reach the max over scroll effect when the user has
// over scrolled half the size of the screen
@@ -1070,7 +1208,7 @@
}
protected void dampedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
float f = (amount / screenSize);
@@ -1130,9 +1268,22 @@
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
+ mDownMotionY = mLastMotionY = ev.getY();
+ mDownScrollX = getScrollX();
+ float[] p = mapPointFromSelfToParent(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();
}
@@ -1164,6 +1315,107 @@
} 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 = mapPointFromSelfToParent(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) (mapPointFromSelfToParent(mViewport.left, 0)[0]
+ + bufferSize);
+ int rightBufferEdge = (int) (mapPointFromSelfToParent(mViewport.right, 0)[0]
+ - bufferSize);
+
+ 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) {
+ 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);
+ addView(mDragView, pageUnderPointIndex);
+ onAddView(mDragView, pageUnderPointIndex);
+ mSidePageHoverIndex = -1;
+ }
+ };
+ postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
+ }
+ } else {
+ removeCallbacks(mSidePageHoverRunnable);
+ mSidePageHoverIndex = -1;
+ }
} else {
determineScrollingStart(ev);
}
@@ -1232,21 +1484,29 @@
} else {
snapToDestination();
}
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ if (!DISABLE_FLING_TO_DELETE) {
+ // Check the velocity and see if we are flinging-to-delete
+ PointF flingToDeleteVector = isFlingingToDelete();
+ if (flingToDeleteVector != null) {
+ onFlingToDelete(flingToDeleteVector);
+ }
+ }
} else {
onUnhandledTap(ev);
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+
+ // 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();
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -1257,6 +1517,20 @@
return true;
}
+ //public abstract void onFlingToDelete(View v);
+ public abstract void onRemoveView(View v);
+ 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) {
@@ -1319,8 +1593,6 @@
}
}
- protected void onUnhandledTap(MotionEvent ev) {}
-
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
@@ -1352,16 +1624,28 @@
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 = getScrollX() + (getMeasuredWidth() / 2);
+ 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 = getChildOffset(i) + halfChildWidth;
+ int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1397,11 +1681,11 @@
protected void snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
- int halfScreenSize = getMeasuredWidth() / 2;
+ int halfScreenSize = getViewportWidth() / 2;
if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
- + getMeasuredWidth() + ", " + getChildWidth(whichPage));
+ + getViewportWidth() + ", " + getChildWidth(whichPage));
final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
int delta = newX - mUnboundedScrollX;
int duration = 0;
@@ -1435,20 +1719,29 @@
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(): " + getMeasuredWidth() + ", "
+ 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);
+ 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;
View focusedChild = getFocusedChild();
@@ -1459,7 +1752,9 @@
pageBeginMoving();
awakenScrollBars(duration);
- if (duration == 0) {
+ if (immediate) {
+ duration = 0;
+ } else if (duration == 0) {
duration = Math.abs(delta);
}
@@ -1467,6 +1762,12 @@
mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
notifyPageSwitchListener();
+
+ // Trigger a compute() to finish switching pages if necessary
+ if (immediate) {
+ computeScroll();
+ }
+
invalidate();
}
@@ -1500,21 +1801,6 @@
return result;
}
- /**
- * @return True is long presses are still allowed for the current touch
- */
- public boolean allowLongPress() {
- return mAllowLongPress;
- }
-
- /**
- * Set true to allow long-press events to be triggered, usually checked by
- * {@link Launcher} to accept or block dpad-initiated long-presses.
- */
- public void setAllowLongPress(boolean allowLongPress) {
- mAllowLongPress = allowLongPress;
- }
-
public static class SavedState extends BaseSavedState {
int currentPage = -1;
@@ -1653,7 +1939,7 @@
if (!isScrollingIndicatorEnabled()) return;
if (mScrollIndicator == null) return;
int numPages = getChildCount();
- int pageWidth = getMeasuredWidth();
+ int pageWidth = getViewportWidth();
int lastChildIndex = Math.max(0, getChildCount() - 1);
int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
@@ -1675,6 +1961,302 @@
mScrollIndicator.setTranslationX(indicatorPos);
}
+ // Animate the drag view back to the original position
+ void animateChildrenToOriginalPosition() {
+ 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.start();
+ }
+ }
+
+ // "Zooms out" the PagedView to reveal more side pages
+ 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.start();
+ return true;
+ }
+ return false;
+ }
+
+ protected void onStartReordering() {
+ // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
+ mTouchState = TOUCH_STATE_REORDERING;
+ mIsReordering = true;
+
+ // We must invalidate to trigger a redraw to update the layers such that the drag view
+ // is always drawn on top
+ invalidate();
+ }
+
+ protected void onEndReordering() {
+ mIsReordering = false;
+ }
+
+ 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;
+ Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onEndReordering();
+ }
+ };
+ zoomIn(onCompleteRunnable);
+
+ // If we haven't flung-to-delete the current child, then we just animate the drag view
+ // back into position
+ if (!mIsFlingingToDelete) {
+ // Snap to the current page
+ snapToDestination();
+
+ animateChildrenToOriginalPosition();
+ }
+ }
+
+ // "Zooms in" the PagedView to highlight the current page
+ 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 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;
+ }
+ };
+
+ 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 = new Runnable() {
+ @Override
+ public void run() {
+ int dragViewIndex = indexOfChild(dragView);
+ // Setup the scroll to the correct page before we swap the views
+ snapToPageImmediately(dragViewIndex - 1);
+
+ // 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
+ int lowerIndex = 0;
+ int upperIndex = dragViewIndex - 1;
+ 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;
+ if (i == 0) {
+ oldX = -(getViewportOffsetX() + getChildOffset(i));
+ } else {
+ oldX = getViewportOffsetX() + getChildOffset(i - 1);
+ }
+ int newX = getViewportOffsetX() + getChildOffset(i);
+
+ // 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(FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsFlingingToDelete = false;
+ }
+ });
+ anim.start();
+ v.setTag(anim);
+ }
+
+ removeView(dragView);
+ onRemoveView(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();
+ mIsFlingingToDelete = true;
+ }
+
/* Accessibility */
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
new file mode 100644
index 0000000..506b79d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -0,0 +1,960 @@
+/*
+ * 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.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+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.animation.Interpolator;
+import android.widget.Scroller;
+
+import com.android.internal.R;
+
+/**
+ * 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;
+
+ // Drawn to show the drag handle in closed state; crossfades to the challenge view
+ // when challenge is fully visible
+ private Drawable mHandleDrawable;
+ private Drawable mFrameDrawable;
+ private Drawable mDragIconDrawable;
+
+ // Initialized during measurement from child layoutparams
+ private View mChallengeView;
+ private View mScrimView;
+
+ // Range: 0 (fully hidden) to 1 (fully visible)
+ private float mChallengeOffset = 1.f;
+ private boolean mChallengeShowing = true;
+ private boolean mIsBouncing = false;
+
+ private final Scroller mScroller;
+ 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;
+
+ 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 mGestureStartY; // where did you touch the screen to start this gesture?
+ private int mGestureStartChallengeBottom; // where was the challenge at that time?
+ private int mDragHandleSize; // handle hitrect extension into the challenge view
+ private int mDragHandleHeadroom; // 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
+ float mHandleAlpha;
+ float mFrameAlpha;
+ private ObjectAnimator mHandleAnimation;
+ private ObjectAnimator mFrameAnimation;
+
+ 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;
+ }
+ };
+
+ static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
+ new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
+ @Override
+ public void setValue(SlidingChallengeLayout view, float value) {
+ if (view.mFrameDrawable != null) {
+ view.mFrameAlpha = value;
+ view.mFrameDrawable.setAlpha((int) (value * 0xFF));
+ view.mFrameDrawable.invalidateSelf();
+ }
+ }
+
+ @Override
+ public Float get(SlidingChallengeLayout view) {
+ return view.mFrameAlpha;
+ }
+ };
+
+ private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
+ private static final int HANDLE_ANIMATE_DURATION = 200; // ms
+
+ // 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();
+ }
+ };
+
+ /**
+ * 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);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SlidingChallengeLayout, defStyle, 0);
+ setDragDrawables(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle),
+ a.getDrawable(R.styleable.SlidingChallengeLayout_dragIcon));
+
+ a.recycle();
+
+ mScroller = new Scroller(context, sMotionInterpolator);
+
+ final ViewConfiguration vc = ViewConfiguration.get(context);
+ mMinVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaxVelocity = vc.getScaledMaximumFlingVelocity();
+
+ mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
+ R.dimen.kg_edge_swipe_region_size);
+
+ setWillNotDraw(false);
+ }
+
+ public void setDragDrawables(Drawable handle, Drawable icon) {
+ final float density = getResources().getDisplayMetrics().density;
+ final int defaultSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
+ final int handleHeight = handle != null ? handle.getIntrinsicHeight() : 0;
+ final int iconHeight = icon != null ? icon.getIntrinsicHeight() : 0;
+ mDragHandleSize = Math.max(handleHeight > 0 ? handleHeight : defaultSize,
+ iconHeight > 0 ? iconHeight : defaultSize);
+
+ // top half of the lock icon, plus another 25% to be sure
+ mDragHandleHeadroom = (int) (iconHeight * 0.75f);
+ mChallengeBottomBound = (mDragHandleSize + mDragHandleHeadroom + handleHeight) / 2;
+
+ mHandleDrawable = handle;
+ mDragIconDrawable = icon;
+ }
+
+ public void setDragIconDrawable(Drawable d) {
+ mDragIconDrawable = d;
+ }
+
+ public void showHandle(boolean visible) {
+ if (visible) {
+ if (mHandleAnimation != null) {
+ mHandleAnimation.cancel();
+ mHandleAnimation = null;
+ }
+ mHandleAlpha = 1.f;
+ invalidate();
+ } else {
+ animateHandle(false);
+ }
+ }
+
+ 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();
+ }
+
+ void animateFrame(boolean visible, boolean full) {
+ if (mFrameDrawable == null) return;
+
+ if (mFrameAnimation != null) {
+ mFrameAnimation.cancel();
+ mFrameAnimation = null;
+ }
+ final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
+ if (targetAlpha == mFrameAlpha) {
+ return;
+ }
+
+ mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
+ mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
+ mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+ mFrameAnimation.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);
+ animateFrame(state != SCROLL_STATE_IDLE, false);
+ if (mScrollListener != null) {
+ mScrollListener.onScrollStateChanged(state);
+ }
+ }
+ }
+
+ void completeChallengeScroll() {
+ setChallengeShowing(mChallengeOffset != 0);
+ setScrollState(SCROLL_STATE_IDLE);
+ }
+
+ 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;
+ }
+ 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) {
+ mChallengeShowing = showChallenge;
+ }
+
+ /**
+ * @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 void showBouncer() {
+ if (mIsBouncing) return;
+ showChallenge(true);
+ mIsBouncing = true;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(VISIBLE);
+ }
+ animateFrame(true, true);
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ setChallengeShowing(false);
+ mIsBouncing = false;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ animateFrame(false, false);
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(false);
+ }
+ }
+
+ @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:
+ 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 &&
+ (isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+ (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
+ mActivePointerId == INVALID_POINTER) {
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartY = ev.getY();
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ } else if (isInChallengeView(x, y)) {
+ mBlockDrag = true;
+ }
+ }
+ break;
+ }
+
+ if (mBlockDrag) {
+ mActivePointerId = INVALID_POINTER;
+ mDragging = false;
+ }
+
+ return mDragging;
+ }
+
+ 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;
+ mGestureStartY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mDragging) {
+ showChallenge(0);
+ }
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ if (mDragging) {
+ 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) {
+ mGestureStartY = y;
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ 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;
+ }
+
+ /**
+ * We only want to add additional vertical space to the drag handle when the panel is fully
+ * closed.
+ */
+ private int getDragHandleHeadroom() {
+ return isChallengeShowing() ? 0 : mDragHandleHeadroom;
+ }
+
+ private boolean isInChallengeView(float x, float y) {
+ if (mChallengeView == null) return false;
+
+ return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
+ x < mChallengeView.getRight() && y < mChallengeView.getBottom();
+ }
+
+ private boolean isInDragHandle(float x, float y) {
+ if (mChallengeView == null) return false;
+
+ return x >= mDragHandleEdgeSlop &&
+ y >= mChallengeView.getTop() - getDragHandleHeadroom() &&
+ x < getWidth() - mDragHandleEdgeSlop &&
+ y < mChallengeView.getTop() + mDragHandleSize;
+ }
+
+ private boolean crossedDragHandle(float x, float y, float initialY) {
+ final int challengeTop = mChallengeView.getTop();
+ return x >= 0 &&
+ x < getWidth() &&
+ initialY < (challengeTop - getDragHandleHeadroom()) &&
+ y > challengeTop + mDragHandleSize;
+ }
+
+ @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;
+ mChallengeView = 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 with layout_isChallenge=\"true\"");
+ }
+ mChallengeView = child;
+ if (mChallengeView != oldChallengeView) {
+ mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
+ }
+ // We're going to play silly games with the frame's background drawable later.
+ mFrameDrawable = mChallengeView.getBackground();
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+ setScrimView(child);
+ }
+
+ if (child.getVisibility() == GONE) continue;
+
+ measureChildWithMargins(child, widthSpec, 0, heightSpec, 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(mChallengeOffset / 2 + 0.5f);
+ child.layout(left, bottom - childHeight, left + childWidth, 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) {
+ // We want to trigger the initial listener updates outside of layout pass,
+ // in case the listeners trigger requestLayout().
+ post(new Runnable() {
+ @Override
+ public void run() {
+ sendInitialListenerUpdates();
+ }
+ });
+ if (mFrameDrawable != null) {
+ mFrameDrawable.setAlpha(0);
+ }
+ mHasLayout = true;
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ super.draw(c);
+
+ final Paint debugPaint;
+ if (DEBUG) {
+ debugPaint = new Paint();
+ debugPaint.setColor(0x40FF00CC);
+ // show the isInDragHandle() rect
+ c.drawRect(mDragHandleEdgeSlop,
+ mChallengeView.getTop() - getDragHandleHeadroom(),
+ getWidth() - mDragHandleEdgeSlop,
+ mChallengeView.getTop() + mDragHandleSize,
+ debugPaint);
+ }
+
+ if (mChallengeView != null && mHandleAlpha > 0 && mHandleDrawable != null) {
+ final int top = mChallengeView.getTop();
+ final int handleHeight = mHandleDrawable.getIntrinsicHeight();
+ final int challengeLeft = mChallengeView.getLeft();
+ final int challengeRight = mChallengeView.getRight();
+ mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
+ mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+ mHandleDrawable.draw(c);
+
+ if (DEBUG) {
+ // now show the actual drag handle
+ debugPaint.setStyle(Paint.Style.STROKE);
+ debugPaint.setStrokeWidth(1);
+ debugPaint.setColor(0xFF80FF00);
+ c.drawRect(challengeLeft, top, challengeRight, top + handleHeight, debugPaint);
+ }
+
+ if (mDragIconDrawable != null) {
+ final int iconWidth = mDragIconDrawable.getIntrinsicWidth();
+ final int iconHeight = mDragIconDrawable.getIntrinsicHeight();
+ final int iconLeft = (challengeLeft + challengeRight - iconWidth) / 2;
+ final int iconTop = top + (handleHeight - iconHeight) / 2;
+ mDragIconDrawable.setBounds(iconLeft, iconTop, iconLeft + iconWidth,
+ iconTop + iconHeight);
+ mDragIconDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+ mDragIconDrawable.draw(c);
+
+ if (DEBUG) {
+ debugPaint.setColor(0xFF00FF00);
+ c.drawRect(iconLeft, iconTop, iconLeft + iconWidth,
+ iconTop + iconHeight, debugPaint);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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(layoutBottom,
+ Math.min(bottom, layoutBottom + challengeHeight - mChallengeBottomBound));
+
+ 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(offset / 2 + 0.5f);
+ 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 = getHeight() - 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) {
+ 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 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);
+ a.recycle();
+ }
+ }
+}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index f960833..440f8e1 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -130,12 +130,14 @@
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mTimeTickSender = PendingIntent.getBroadcast(context, 0,
+ mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0,
+ UserHandle.ALL);
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
+ mDateChangeSender = PendingIntent.getBroadcastAsUser(context, 0, intent,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
// now that we have initied the driver schedule the alarm
mClockReceiver= new ClockReceiver();
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index dcbf3f5..b38b016 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1434,12 +1434,9 @@
set.add(pkg.packageName);
if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
- // If we've never seen this app before, schedule a backup for it
- if (!mEverStoredApps.contains(pkg.packageName)) {
- if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
- + " never backed up; scheduling");
- dataChangedImpl(pkg.packageName);
- }
+ // Schedule a backup for it on general principles
+ if (DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+ dataChangedImpl(pkg.packageName);
}
}
}
@@ -1472,8 +1469,10 @@
// Found it. Remove this one package from the bookkeeping, and
// if it's the last participating app under this uid we drop the
// (now-empty) set as well.
+ // Note that we deliberately leave it 'known' in the "ever backed up"
+ // bookkeeping so that its current-dataset data will be retrieved
+ // if the app is subsequently reinstalled
if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
- removeEverBackedUp(packageName);
set.remove(packageName);
mPendingBackups.remove(packageName);
}
@@ -5441,7 +5440,8 @@
long restoreSet = getAvailableRestoreToken(packageName);
if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
- + " token=" + Integer.toHexString(token));
+ + " token=" + Integer.toHexString(token)
+ + " restoreSet=" + Long.toHexString(restoreSet));
if (mAutoRestore && mProvisioned && restoreSet != 0) {
// okay, we're going to attempt a restore of this package from this restore set.
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 079f723..6ff33d7 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -1,5 +1,17 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * 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.server;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 679a22a..0e51c47 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1561,7 +1561,12 @@
intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
return true;
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 37dee19..eb833eb 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -405,7 +405,9 @@
mLastLocation.clear();
for (LocationProviderInterface p : mProviders) {
updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
- p.switchUser(userId);
+ if (!LocationManager.FUSED_PROVIDER.equals(p.getName())) {
+ p.switchUser(userId);
+ }
}
mCurrentUserId = userId;
updateProvidersLocked();
@@ -664,9 +666,27 @@
mProvidersByName.remove(provider.getName());
}
+ /**
+ * Returns true if the specified UID is SYSTEM_UID or matches the current user.
+ *
+ * @param uid the uid
+ * @return true if uid is SYSTEM_UID or matches the current user
+ */
+ private boolean isCurrentUserOrSystemLocked(int uid) {
+ return uid == Process.SYSTEM_UID || UserHandle.getUserId(uid) == mCurrentUserId;
+ }
- private boolean isAllowedBySettingsLocked(String provider, int userId) {
- if (userId != mCurrentUserId) {
+ /**
+ * Returns the first UID in the current user's range.
+ *
+ * @return the first UID in the current user's range
+ */
+ private int getCurrentUidBaseLocked() {
+ return UserHandle.getUid(mCurrentUserId, 0);
+ }
+
+ private boolean isAllowedBySettingsLocked(String provider, int uid) {
+ if (!isCurrentUserOrSystemLocked(uid)) {
return false;
}
if (mEnabledProviders.contains(provider)) {
@@ -675,6 +695,10 @@
if (mDisabledProviders.contains(provider)) {
return false;
}
+ if (uid == Process.SYSTEM_UID) {
+ return true;
+ }
+
// Use system settings
ContentResolver resolver = mContext.getContentResolver();
@@ -828,8 +852,8 @@
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
ArrayList<String> out;
- int callingUserId = UserHandle.getCallingUserId();
- long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
out = new ArrayList<String>(mProviders.size());
@@ -839,7 +863,7 @@
continue;
}
if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
- if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
+ if (enabledOnly && !isAllowedBySettingsLocked(name, callingUid)) {
continue;
}
if (criteria != null && !LocationProvider.propertiesMeetCriteria(
@@ -915,7 +939,7 @@
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
- boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
+ boolean shouldBeEnabled = isAllowedBySettingsLocked(name, getCurrentUidBaseLocked());
if (isEnabled && !shouldBeEnabled) {
updateProviderListenersLocked(name, false, mCurrentUserId);
changesMade = true;
@@ -943,7 +967,7 @@
final int N = records.size();
for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
- if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+ if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
@@ -982,7 +1006,7 @@
if (records != null) {
for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
LocationRequest locationRequest = record.mRequest;
providerRequest.locationRequests.add(locationRequest);
if (locationRequest.getInterval() < providerRequest.interval) {
@@ -1000,7 +1024,7 @@
// under that threshold.
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
LocationRequest locationRequest = record.mRequest;
if (locationRequest.getInterval() <= thresholdInterval) {
worksource.add(record.mReceiver.mUid);
@@ -1223,7 +1247,7 @@
oldRecord.disposeLocked(false);
}
- boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
+ boolean isProviderEnabled = isAllowedBySettingsLocked(name, uid);
if (isProviderEnabled) {
applyRequirementsLocked(name);
} else {
@@ -1278,9 +1302,10 @@
}
// update provider
+ int currentUidBase = getCurrentUidBaseLocked();
for (String provider : providers) {
// If provider is already disabled, don't need to do anything
- if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
+ if (!isAllowedBySettingsLocked(provider, currentUidBase)) {
continue;
}
@@ -1298,7 +1323,8 @@
request.getProvider());
// no need to sanitize this request, as only the provider name is used
- long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
try {
if (mBlacklist.isBlacklisted(packageName)) {
if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
@@ -1314,7 +1340,9 @@
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) return null;
- if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
+ if (!isAllowedBySettingsLocked(name, callingUid)) {
+ return null;
+ }
Location location = mLastLocation.get(name);
if (location == null) {
@@ -1471,13 +1499,14 @@
provider);
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
- long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return false;
- return isAllowedBySettingsLocked(provider, mCurrentUserId);
+ return isAllowedBySettingsLocked(provider, callingUid);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1616,10 +1645,10 @@
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- int receiverUserId = UserHandle.getUserId(receiver.mUid);
- if (receiverUserId != mCurrentUserId) {
+ final int receiverUid = receiver.mUid;
+ if (!isCurrentUserOrSystemLocked(receiverUid)) {
if (D) {
- Log.d(TAG, "skipping loc update for background user " + receiverUserId +
+ Log.d(TAG, "skipping loc update for background uid " + receiverUid +
" (current user: " + mCurrentUserId + ", app: " +
receiver.mPackageName + ")");
}
@@ -1716,7 +1745,7 @@
}
synchronized (mLock) {
- if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
+ if (isAllowedBySettingsLocked(provider, getCurrentUidBaseLocked())) {
handleLocationChangedLocked(location, passive);
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7c482f5..6b277c7 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -558,8 +558,9 @@
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
// Automation service is not bound, so pretend it died to perform clean up.
- if (mUiAutomationService != null
- && mUiAutomationService.mServiceInterface == serviceClient) {
+ if (mUiAutomationService != null && mUiAutomationService.mServiceInterface != null
+ && serviceClient != null && mUiAutomationService.mServiceInterface
+ .asBinder() == serviceClient.asBinder()) {
mUiAutomationService.binderDied();
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index b9f5b5b..749dc66 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -602,6 +602,15 @@
}
}
+ void updateOptionsLocked(ActivityOptions options) {
+ if (options != null) {
+ if (pendingOptions != null) {
+ pendingOptions.abort();
+ }
+ pendingOptions = options;
+ }
+ }
+
void applyOptionsLocked() {
if (pendingOptions != null) {
final int animationType = pendingOptions.getAnimationType();
@@ -653,6 +662,12 @@
}
}
+ ActivityOptions takeOptionsLocked() {
+ ActivityOptions opts = pendingOptions;
+ pendingOptions = null;
+ return opts;
+ }
+
void removeUriPermissionsLocked() {
if (uriPermissions != null) {
uriPermissions.removeUriPermissionsLocked();
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 4bcb339..4546dc3 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1963,6 +1963,8 @@
int taskTopI = -1;
int replyChainEnd = -1;
int lastReparentPos = -1;
+ ActivityOptions topOptions = null;
+ boolean canMoveOptions = true;
for (int i=mHistory.size()-1; i>=-1; i--) {
ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
@@ -2048,6 +2050,7 @@
}
int dstPos = 0;
ThumbnailHolder curThumbHolder = target.thumbHolder;
+ boolean gotOptions = !canMoveOptions;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
p = mHistory.get(srcPos);
if (p.finishing) {
@@ -2057,6 +2060,13 @@
+ " 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();
@@ -2101,11 +2111,19 @@
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--;
@@ -2245,7 +2263,17 @@
target = below;
targetI = i;
}
-
+
+ if (topOptions != null) {
+ // If we got some ActivityOptions from an activity on top that
+ // was removed from the task, propagate them to the new real top.
+ if (taskTop != null) {
+ taskTop.updateOptionsLocked(topOptions);
+ } else {
+ topOptions.abort();
+ }
+ }
+
return taskTop;
}
@@ -2296,6 +2324,10 @@
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--;
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index e76bf44..247d8a0 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.Surface;
import libcore.util.Objects;
@@ -138,6 +139,17 @@
*/
public int rotation = Surface.ROTATION_0;
+ /**
+ * Display type.
+ */
+ public int type;
+
+ /**
+ * Display address, or null if none.
+ * Interpretation varies by display type.
+ */
+ public String address;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -162,7 +174,9 @@
&& yDpi == other.yDpi
&& flags == other.flags
&& touch == other.touch
- && rotation == other.rotation;
+ && rotation == other.rotation
+ && type == other.type
+ && Objects.equal(address, other.address);
}
@Override
@@ -181,6 +195,8 @@
flags = other.flags;
touch = other.touch;
rotation = other.rotation;
+ type = other.type;
+ address = other.address;
}
// For debugging purposes
@@ -191,6 +207,8 @@
+ "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
+ ", touch " + touchToString(touch) + flagsToString(flags)
+ ", rotation " + rotation
+ + ", type " + Display.typeToString(type)
+ + ", address " + address
+ "}";
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index 919733d..7a104d7 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.Handler;
import android.util.DisplayMetrics;
+import android.view.Display;
/**
* Provides a fake default display for headless systems.
@@ -63,6 +64,7 @@
mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ mInfo.type = Display.TYPE_BUILT_IN;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index d6c5248..b37d57f 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,7 @@
import android.os.Looper;
import android.os.SystemProperties;
import android.util.SparseArray;
+import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.Surface.PhysicalDisplayInfo;
@@ -127,21 +128,25 @@
mInfo.height = mPhys.height;
mInfo.refreshRate = mPhys.refreshRate;
- // Assume that all built-in displays have secure output (eg. HDCP) and
+ // Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
- mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
- | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ if (mPhys.secure) {
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
+ | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ }
if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
mInfo.xDpi = mPhys.xDpi;
mInfo.yDpi = mPhys.yDpi;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
} else {
+ mInfo.type = Display.TYPE_HDMI;
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index aa7ea82..1583137 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -186,6 +186,11 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
+ }
+ mBaseDisplayInfo.type = deviceInfo.type;
+ mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.appWidth = deviceInfo.width;
mBaseDisplayInfo.appHeight = deviceInfo.height;
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index c35fd98..36e9f74 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -27,6 +27,7 @@
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.view.Display;
import android.view.Gravity;
import android.view.Surface;
@@ -240,6 +241,7 @@
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
mInfo.flags = 0;
+ mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 97fc3e6..45fff30 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -39,6 +39,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
+import android.view.Display;
import android.view.Surface;
import java.io.PrintWriter;
@@ -281,20 +282,22 @@
scheduleStatusChangedBroadcastLocked();
}
+ boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
int deviceFlags = 0;
- if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
+ if (secure) {
deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
- }
- if (mSupportsProtectedBuffers) {
- deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ if (mSupportsProtectedBuffers) {
+ deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ }
}
float refreshRate = 60.0f; // TODO: get this for real
String name = display.getFriendlyDisplayName();
- IBinder displayToken = Surface.createDisplay(name, false);
+ String address = display.getDeviceAddress();
+ IBinder displayToken = Surface.createDisplay(name, secure);
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
- refreshRate, deviceFlags, surface);
+ refreshRate, deviceFlags, address, surface);
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
scheduleUpdateNotificationLocked();
@@ -514,12 +517,13 @@
private final int mHeight;
private final float mRefreshRate;
private final int mFlags;
+ private final String mAddress;
private Surface mSurface;
private DisplayDeviceInfo mInfo;
public WifiDisplayDevice(IBinder displayToken, String name,
- int width, int height, float refreshRate, int flags,
+ int width, int height, float refreshRate, int flags, String address,
Surface surface) {
super(WifiDisplayAdapter.this, displayToken);
mName = name;
@@ -527,6 +531,7 @@
mHeight = height;
mRefreshRate = refreshRate;
mFlags = flags;
+ mAddress = address;
mSurface = surface;
}
@@ -554,6 +559,8 @@
mInfo.height = mHeight;
mInfo.refreshRate = mRefreshRate;
mInfo.flags = mFlags;
+ mInfo.type = Display.TYPE_WIFI;
+ mInfo.address = mAddress;
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index fb93d05..e05442b 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -26,6 +26,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -75,6 +76,7 @@
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
+ private static final String ATTR_USER_VERSION = "version";
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
@@ -84,6 +86,8 @@
private static final int MIN_USER_ID = 10;
+ private static final int USER_VERSION = 1;
+
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
private final Context mContext;
@@ -104,6 +108,7 @@
// This resets on a reboot. Otherwise it keeps incrementing so that user ids are
// not reused in quick succession
private int mNextUserId = MIN_USER_ID;
+ private int mUserVersion = 0;
private static UserManagerService sInstance;
@@ -432,12 +437,17 @@
if (lastSerialNumber != null) {
mNextSerialNumber = Integer.parseInt(lastSerialNumber);
}
+ String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
+ if (versionNumber != null) {
+ mUserVersion = Integer.parseInt(versionNumber);
+ }
}
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
UserInfo user = readUser(Integer.parseInt(id));
+
if (user != null) {
mUsers.put(user.id, user);
if (user.isGuest()) {
@@ -450,6 +460,7 @@
}
}
updateUserIdsLocked();
+ upgradeIfNecessary();
} catch (IOException ioe) {
fallbackToSingleUserLocked();
} catch (XmlPullParserException pe) {
@@ -464,9 +475,35 @@
}
}
+ /**
+ * This fixes an incorrect initialization of user name for the owner.
+ * TODO: Remove in the next release.
+ */
+ private void upgradeIfNecessary() {
+ int userVersion = mUserVersion;
+ if (userVersion < 1) {
+ // Assign a proper name for the owner, if not initialized correctly before
+ UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+ if ("Primary".equals(user.name)) {
+ user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ writeUserLocked(user);
+ }
+ userVersion = 1;
+ }
+
+ if (userVersion < USER_VERSION) {
+ Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ + USER_VERSION);
+ } else {
+ mUserVersion = userVersion;
+ writeUserListLocked();
+ }
+ }
+
private void fallbackToSingleUserLocked() {
// Create the primary user
- UserInfo primary = new UserInfo(0, "Primary", null,
+ UserInfo primary = new UserInfo(0,
+ mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
mUsers.put(0, primary);
mNextSerialNumber = MIN_USER_ID;
@@ -547,6 +584,7 @@
serializer.startTag(null, TAG_USERS);
serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
+ serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
for (int i = 0; i < mUsers.size(); i++) {
UserInfo user = mUsers.valueAt(i);
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 4abd8f5..317fec0 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -648,10 +648,10 @@
mUsingScreenAutoBrightness = false;
}
if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
- // Dim slowly by at least some minimum amount.
+ // Dim quickly by at least some minimum amount.
target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION,
mScreenBrightnessDimConfig);
- slow = true;
+ slow = false;
} else if (wasDim) {
// Brighten quickly.
slow = false;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 03a0434..039319d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -1566,9 +1566,17 @@
WifiP2pManager.ERROR);
}
break;
- /* The supplicant misses the group removed event at times and just
- * sends a network disconnect event */
- case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ /* We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
+ * handling since supplicant actually tries to reconnect after a temporary
+ * disconnect until group idle time out. Eventually, a group removal event
+ * will come when group has been removed.
+ *
+ * When there are connectivity issues during temporary disconnect, the application
+ * will also just remove the group.
+ *
+ * Treating network disconnection as group removal causes race conditions since
+ * supplicant would still maintain the group at that stage.
+ */
case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
if (DBG) logd(getName() + " group removed");
handleGroupRemoved();