diff options
86 files changed, 2414 insertions, 537 deletions
@@ -491,6 +491,8 @@ package android { field public static final int foreground = 16843017; // 0x1010109 field public static final int foregroundGravity = 16843264; // 0x1010200 field public static final int format = 16843013; // 0x1010105 + field public static final int format12Hour = 16843722; // 0x10103ca + field public static final int format24Hour = 16843723; // 0x10103cb field public static final int fragment = 16843491; // 0x10102e3 field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7 field public static final int fragmentCloseExitAnimation = 16843496; // 0x10102e8 @@ -1074,6 +1076,7 @@ package android { field public static final int thumbTextPadding = 16843634; // 0x1010372 field public static final int thumbnail = 16843429; // 0x10102a5 field public static final int tileMode = 16843265; // 0x1010201 + field public static final int timeZone = 16843724; // 0x10103cc field public static final int tint = 16843041; // 0x1010121 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 @@ -22300,14 +22303,14 @@ package android.text.format { public class DateFormat { ctor public DateFormat(); - method public static final java.lang.CharSequence format(java.lang.CharSequence, long); - method public static final java.lang.CharSequence format(java.lang.CharSequence, java.util.Date); - method public static final java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar); - method public static final java.text.DateFormat getDateFormat(android.content.Context); - method public static final char[] getDateFormatOrder(android.content.Context); - method public static final java.text.DateFormat getLongDateFormat(android.content.Context); - method public static final java.text.DateFormat getMediumDateFormat(android.content.Context); - method public static final java.text.DateFormat getTimeFormat(android.content.Context); + method public static java.lang.CharSequence format(java.lang.CharSequence, long); + method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Date); + method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar); + method public static java.text.DateFormat getDateFormat(android.content.Context); + method public static char[] getDateFormatOrder(android.content.Context); + method public static java.text.DateFormat getLongDateFormat(android.content.Context); + method public static java.text.DateFormat getMediumDateFormat(android.content.Context); + method public static java.text.DateFormat getTimeFormat(android.content.Context); method public static boolean is24HourFormat(android.content.Context); field public static final char AM_PM = 97; // 0x0061 'a' field public static final char CAPITAL_AM_PM = 65; // 0x0041 'A' @@ -29518,6 +29521,21 @@ package android.widget { field public int span; } + public class TextClock extends android.widget.TextView { + ctor public TextClock(android.content.Context); + ctor public TextClock(android.content.Context, android.util.AttributeSet); + ctor public TextClock(android.content.Context, android.util.AttributeSet, int); + method public java.lang.CharSequence getFormat12Hour(); + method public java.lang.CharSequence getFormat24Hour(); + method public java.lang.String getTimeZone(); + method public boolean is24HourModeEnabled(); + method public void setFormat12Hour(java.lang.CharSequence); + method public void setFormat24Hour(java.lang.CharSequence); + method public void setTimeZone(java.lang.String); + field public static final java.lang.CharSequence DEFAULT_FORMAT_12_HOUR; + field public static final java.lang.CharSequence DEFAULT_FORMAT_24_HOUR; + } + public class TextSwitcher extends android.widget.ViewSwitcher { ctor public TextSwitcher(android.content.Context); ctor public TextSwitcher(android.content.Context, android.util.AttributeSet); diff --git a/api/current.txt b/api/current.txt index e26d8f0..fde6302 100644 --- a/api/current.txt +++ b/api/current.txt @@ -491,6 +491,8 @@ package android { field public static final int foreground = 16843017; // 0x1010109 field public static final int foregroundGravity = 16843264; // 0x1010200 field public static final int format = 16843013; // 0x1010105 + field public static final int format12Hour = 16843722; // 0x10103ca + field public static final int format24Hour = 16843723; // 0x10103cb field public static final int fragment = 16843491; // 0x10102e3 field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7 field public static final int fragmentCloseExitAnimation = 16843496; // 0x10102e8 @@ -1074,6 +1076,7 @@ package android { field public static final int thumbTextPadding = 16843634; // 0x1010372 field public static final int thumbnail = 16843429; // 0x10102a5 field public static final int tileMode = 16843265; // 0x1010201 + field public static final int timeZone = 16843724; // 0x10103cc field public static final int tint = 16843041; // 0x1010121 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 @@ -22300,14 +22303,14 @@ package android.text.format { public class DateFormat { ctor public DateFormat(); - method public static final java.lang.CharSequence format(java.lang.CharSequence, long); - method public static final java.lang.CharSequence format(java.lang.CharSequence, java.util.Date); - method public static final java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar); - method public static final java.text.DateFormat getDateFormat(android.content.Context); - method public static final char[] getDateFormatOrder(android.content.Context); - method public static final java.text.DateFormat getLongDateFormat(android.content.Context); - method public static final java.text.DateFormat getMediumDateFormat(android.content.Context); - method public static final java.text.DateFormat getTimeFormat(android.content.Context); + method public static java.lang.CharSequence format(java.lang.CharSequence, long); + method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Date); + method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar); + method public static java.text.DateFormat getDateFormat(android.content.Context); + method public static char[] getDateFormatOrder(android.content.Context); + method public static java.text.DateFormat getLongDateFormat(android.content.Context); + method public static java.text.DateFormat getMediumDateFormat(android.content.Context); + method public static java.text.DateFormat getTimeFormat(android.content.Context); method public static boolean is24HourFormat(android.content.Context); field public static final char AM_PM = 97; // 0x0061 'a' field public static final char CAPITAL_AM_PM = 65; // 0x0041 'A' @@ -29518,6 +29521,21 @@ package android.widget { field public int span; } + public class TextClock extends android.widget.TextView { + ctor public TextClock(android.content.Context); + ctor public TextClock(android.content.Context, android.util.AttributeSet); + ctor public TextClock(android.content.Context, android.util.AttributeSet, int); + method public java.lang.CharSequence getFormat12Hour(); + method public java.lang.CharSequence getFormat24Hour(); + method public java.lang.String getTimeZone(); + method public boolean is24HourModeEnabled(); + method public void setFormat12Hour(java.lang.CharSequence); + method public void setFormat24Hour(java.lang.CharSequence); + method public void setTimeZone(java.lang.String); + field public static final java.lang.CharSequence DEFAULT_FORMAT_12_HOUR; + field public static final java.lang.CharSequence DEFAULT_FORMAT_24_HOUR; + } + public class TextSwitcher extends android.widget.ViewSwitcher { ctor public TextSwitcher(android.content.Context); ctor public TextSwitcher(android.content.Context, android.util.AttributeSet); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 2af65b9..3dd640c 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -80,6 +80,13 @@ public class AppWidgetManager { public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK"; /** + * Similar to ACTION_APPWIDGET_PICK, but used from keyguard + * @hide + */ + public static final String + ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK"; + + /** * Send this from your {@link AppWidgetHost} activity when you want to bind an AppWidget to * display and bindAppWidgetIdIfAllowed returns false. * <p> @@ -224,13 +231,6 @@ public class AppWidgetManager { public static final String EXTRA_CATEGORY_FILTER = "categoryFilter"; /** - * An intent extra to pass to the AppWidget picker which allows the picker to filter - * the list based on the {@link AppWidgetProviderInfo#widgetFeatures}. - * @hide - */ - public static final String EXTRA_FEATURES_FILTER = "featuresFilter"; - - /** * An intent extra to pass to the AppWidget picker to specify whether or not to sort * the list of caller-specified extra AppWidgets along with the rest of the AppWidgets * @hide diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index c36273e..3c984b5 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -249,12 +249,13 @@ public class DateFormat { synchronized (sLocaleLock) { sIs24HourLocale = locale; - sIs24Hour = !value.equals("12"); + sIs24Hour = value.equals("24"); } + + return sIs24Hour; } - boolean b24 = !(value == null || value.equals("12")); - return b24; + return value.equals("24"); } /** @@ -263,7 +264,7 @@ public class DateFormat { * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the time. */ - public static final java.text.DateFormat getTimeFormat(Context context) { + public static java.text.DateFormat getTimeFormat(Context context) { boolean b24 = is24HourFormat(context); int res; @@ -283,7 +284,7 @@ public class DateFormat { * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the date. */ - public static final java.text.DateFormat getDateFormat(Context context) { + public static java.text.DateFormat getDateFormat(Context context) { String value = Settings.System.getString(context.getContentResolver(), Settings.System.DATE_FORMAT); @@ -353,7 +354,7 @@ public class DateFormat { * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ - public static final java.text.DateFormat getLongDateFormat(Context context) { + public static java.text.DateFormat getLongDateFormat(Context context) { return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG); } @@ -363,7 +364,7 @@ public class DateFormat { * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ - public static final java.text.DateFormat getMediumDateFormat(Context context) { + public static java.text.DateFormat getMediumDateFormat(Context context) { return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM); } @@ -376,7 +377,7 @@ public class DateFormat { * not just the day, month, and year, and not necessarily in the same * order returned here. */ - public static final char[] getDateFormatOrder(Context context) { + public static char[] getDateFormatOrder(Context context) { char[] order = new char[] {DATE, MONTH, YEAR}; String value = getDateFormatString(context); int index = 0; @@ -420,7 +421,7 @@ public class DateFormat { * @param inTimeInMillis in milliseconds since Jan 1, 1970 GMT * @return a {@link CharSequence} containing the requested text */ - public static final CharSequence format(CharSequence inFormat, long inTimeInMillis) { + public static CharSequence format(CharSequence inFormat, long inTimeInMillis) { return format(inFormat, new Date(inTimeInMillis)); } @@ -431,7 +432,7 @@ public class DateFormat { * @param inDate the date to format * @return a {@link CharSequence} containing the requested text */ - public static final CharSequence format(CharSequence inFormat, Date inDate) { + public static CharSequence format(CharSequence inFormat, Date inDate) { Calendar c = new GregorianCalendar(); c.setTime(inDate); @@ -440,13 +441,75 @@ public class DateFormat { } /** + * Indicates whether the specified format string contains seconds. + * + * Always returns false if the input format is null. + * + * @param inFormat the format string, as described in {@link android.text.format.DateFormat} + * + * @return true if the format string contains {@link #SECONDS}, false otherwise + * + * @hide + */ + public static boolean hasSeconds(CharSequence inFormat) { + if (inFormat == null) return false; + + final int length = inFormat.length(); + + int c; + int count; + + for (int i = 0; i < length; i += count) { + count = 1; + c = inFormat.charAt(i); + + if (c == QUOTE) { + count = skipQuotedText(inFormat, i, length); + } else if (c == SECONDS) { + return true; + } + } + + return false; + } + + private static int skipQuotedText(CharSequence s, int i, int len) { + if (i + 1 < len && s.charAt(i + 1) == QUOTE) { + return 2; + } + + int count = 1; + // skip leading quote + i++; + + while (i < len) { + char c = s.charAt(i); + + if (c == QUOTE) { + count++; + // QUOTEQUOTE -> QUOTE + if (i + 1 < len && s.charAt(i + 1) == QUOTE) { + i++; + } else { + break; + } + } else { + i++; + count++; + } + } + + return count; + } + + /** * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence * containing the requested date. * @param inFormat the format string, as described in {@link android.text.format.DateFormat} * @param inDate the date to format * @return a {@link CharSequence} containing the requested text */ - public static final CharSequence format(CharSequence inFormat, Calendar inDate) { + public static CharSequence format(CharSequence inFormat, Calendar inDate) { SpannableStringBuilder s = new SpannableStringBuilder(inFormat); int c; int count; @@ -545,7 +608,7 @@ public class DateFormat { return s.toString(); } - private static final String getMonthString(Calendar inDate, int count, int kind) { + private static String getMonthString(Calendar inDate, int count, int kind) { boolean standalone = (kind == STANDALONE_MONTH); int month = inDate.get(Calendar.MONTH); @@ -563,7 +626,7 @@ public class DateFormat { } } - private static final String getTimeZoneString(Calendar inDate, int count) { + private static String getTimeZoneString(Calendar inDate, int count) { TimeZone tz = inDate.getTimeZone(); if (count < 2) { // FIXME: shouldn't this be <= 2 ? @@ -576,7 +639,7 @@ public class DateFormat { } } - private static final String formatZoneOffset(int offset, int count) { + private static String formatZoneOffset(int offset, int count) { offset /= 1000; // milliseconds to seconds StringBuilder tb = new StringBuilder(); @@ -595,13 +658,13 @@ public class DateFormat { return tb.toString(); } - private static final String getYearString(Calendar inDate, int count) { + private static String getYearString(Calendar inDate, int count) { int year = inDate.get(Calendar.YEAR); return (count <= 2) ? zeroPad(year % 100, 2) : String.format(Locale.getDefault(), "%d", year); } - private static final int appendQuotedText(SpannableStringBuilder s, int i, int len) { + private static int appendQuotedText(SpannableStringBuilder s, int i, int len) { if (i + 1 < len && s.charAt(i + 1) == QUOTE) { s.delete(i, i + 1); return 1; @@ -638,7 +701,7 @@ public class DateFormat { return count; } - private static final String zeroPad(int inValue, int inMinDigits) { + private static String zeroPad(int inValue, int inMinDigits) { return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9d0d4f0..d5e1ed3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17697,7 +17697,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mScalingRequired; /** - * If set, ViewAncestor doesn't use its lame animation for when the window resizes. + * If set, ViewRootImpl doesn't use its lame animation for when the window resizes. */ boolean mTurnOffWindowResizeAnim; diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index 95a0416..008a615 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -18,6 +18,7 @@ package android.webkit; import android.content.Context; import android.os.Bundle; +import android.os.Handler; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; @@ -159,7 +160,7 @@ class AccessibilityInjector { * <p> * This should only be called before a page loads. */ - private void addAccessibilityApisIfNecessary() { + public void addAccessibilityApisIfNecessary() { if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) { return; } @@ -333,8 +334,9 @@ class AccessibilityInjector { */ public void onPageStarted(String url) { mAccessibilityScriptInjected = false; - if (DEBUG) + if (DEBUG) { Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page"); + } addAccessibilityApisIfNecessary(); } @@ -348,30 +350,57 @@ class AccessibilityInjector { */ public void onPageFinished(String url) { if (!isAccessibilityEnabled()) { - mAccessibilityScriptInjected = false; toggleFallbackAccessibilityInjector(false); return; } - if (!shouldInjectJavaScript(url)) { - mAccessibilityScriptInjected = false; - toggleFallbackAccessibilityInjector(true); - if (DEBUG) - Log.d(TAG, "[" + mWebView.hashCode() + "] Using fallback accessibility support"); - return; + toggleFallbackAccessibilityInjector(true); + + if (shouldInjectJavaScript(url)) { + // If we're supposed to use the JS screen reader, request a + // callback to confirm that CallbackHandler is working. + if (DEBUG) { + Log.d(TAG, "[" + mWebView.hashCode() + "] Request callback "); + } + + mCallback.requestCallback(mWebView, mInjectScriptRunnable); + } + } + + /** + * Runnable used to inject the JavaScript-based screen reader if the + * {@link CallbackHandler} API was successfully exposed to JavaScript. + */ + private Runnable mInjectScriptRunnable = new Runnable() { + @Override + public void run() { + if (DEBUG) { + Log.d(TAG, "[" + mWebView.hashCode() + "] Received callback"); + } + + injectJavaScript(); } + }; + /** + * Called by {@link #mInjectScriptRunnable} to inject the JavaScript-based + * screen reader after confirming that the {@link CallbackHandler} API is + * functional. + */ + private void injectJavaScript() { toggleFallbackAccessibilityInjector(false); if (!mAccessibilityScriptInjected) { mAccessibilityScriptInjected = true; final String injectionUrl = getScreenReaderInjectionUrl(); mWebView.loadUrl(injectionUrl); - if (DEBUG) + if (DEBUG) { Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView"); + } } else { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice"); + } } } @@ -447,12 +476,10 @@ class AccessibilityInjector { * been done. */ private void addTtsApis() { - if (mTextToSpeech != null) { - return; + if (mTextToSpeech == null) { + mTextToSpeech = new TextToSpeechWrapper(mContext); } - if (DEBUG) - Log.d(TAG, "[" + mWebView.hashCode() + "] Adding TTS APIs into WebView"); - mTextToSpeech = new TextToSpeechWrapper(mContext); + mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE); } @@ -461,34 +488,29 @@ class AccessibilityInjector { * already been done. */ private void removeTtsApis() { - if (mTextToSpeech == null) { - return; + if (mTextToSpeech != null) { + mTextToSpeech.stop(); + mTextToSpeech.shutdown(); + mTextToSpeech = null; } - if (DEBUG) - Log.d(TAG, "[" + mWebView.hashCode() + "] Removing TTS APIs from WebView"); mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE); - mTextToSpeech.stop(); - mTextToSpeech.shutdown(); - mTextToSpeech = null; } private void addCallbackApis() { - if (mCallback != null) { - return; + if (mCallback == null) { + mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE); } - mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE); mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE); } private void removeCallbackApis() { - if (mCallback == null) { - return; + if (mCallback != null) { + mCallback = null; } mWebView.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE); - mCallback = null; } /** @@ -638,9 +660,10 @@ class AccessibilityInjector { private volatile boolean mShutdown; public TextToSpeechWrapper(Context context) { - if (DEBUG) + if (DEBUG) { Log.d(WRAP_TAG, "[" + hashCode() + "] Initializing text-to-speech on thread " + Thread.currentThread().getId() + "..."); + } final String pkgName = context.getPackageName(); @@ -672,12 +695,14 @@ class AccessibilityInjector { public int speak(String text, int queueMode, HashMap<String, String> params) { synchronized (mTextToSpeech) { if (!mReady) { - if (DEBUG) + if (DEBUG) { Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to speak before TTS init"); + } return TextToSpeech.ERROR; } else { - if (DEBUG) + if (DEBUG) { Log.i(WRAP_TAG, "[" + hashCode() + "] Speak called from JS binder"); + } } return mTextToSpeech.speak(text, queueMode, params); @@ -689,12 +714,14 @@ class AccessibilityInjector { public int stop() { synchronized (mTextToSpeech) { if (!mReady) { - if (DEBUG) + if (DEBUG) { Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to stop before initialize"); + } return TextToSpeech.ERROR; } else { - if (DEBUG) + if (DEBUG) { Log.i(WRAP_TAG, "[" + hashCode() + "] Stop called from JS binder"); + } } return mTextToSpeech.stop(); @@ -705,12 +732,14 @@ class AccessibilityInjector { protected void shutdown() { synchronized (mTextToSpeech) { if (!mReady) { - if (DEBUG) + if (DEBUG) { Log.w(WRAP_TAG, "[" + hashCode() + "] Called shutdown before initialize"); + } } else { - if (DEBUG) + if (DEBUG) { Log.i(WRAP_TAG, "[" + hashCode() + "] Shutting down text-to-speech from " + "thread " + Thread.currentThread().getId() + "..."); + } } mShutdown = true; mReady = false; @@ -723,14 +752,16 @@ class AccessibilityInjector { public void onInit(int status) { synchronized (mTextToSpeech) { if (!mShutdown && (status == TextToSpeech.SUCCESS)) { - if (DEBUG) + if (DEBUG) { Log.d(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + "] Initialized successfully"); + } mReady = true; } else { - if (DEBUG) + if (DEBUG) { Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + "] Failed to initialize"); + } mReady = false; } } @@ -745,9 +776,10 @@ class AccessibilityInjector { @Override public void onError(String utteranceId) { - if (DEBUG) + if (DEBUG) { Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + "] Failed to speak utterance"); + } } @Override @@ -770,12 +802,16 @@ class AccessibilityInjector { private final AtomicInteger mResultIdCounter = new AtomicInteger(); private final Object mResultLock = new Object(); private final String mInterfaceName; + private final Handler mMainHandler; + + private Runnable mCallbackRunnable; private boolean mResult = false; private int mResultId = -1; private CallbackHandler(String interfaceName) { mInterfaceName = interfaceName; + mMainHandler = new Handler(); } /** @@ -826,25 +862,29 @@ class AccessibilityInjector { private boolean waitForResultTimedLocked(int resultId) { final long startTimeMillis = SystemClock.uptimeMillis(); - if (DEBUG) + if (DEBUG) { Log.d(TAG, "Waiting for CVOX result with ID " + resultId + "..."); + } while (true) { // Fail if we received a callback from the future. if (mResultId > resultId) { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Aborted CVOX result"); + } return false; } final long elapsedTimeMillis = (SystemClock.uptimeMillis() - startTimeMillis); // Succeed if we received the callback we were expecting. - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Check " + mResultId + " versus expected " + resultId); + } if (mResultId == resultId) { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Received CVOX result after " + elapsedTimeMillis + " ms"); + } return true; } @@ -852,18 +892,21 @@ class AccessibilityInjector { // Fail if we've already exceeded the timeout. if (waitTimeMillis <= 0) { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Timed out while waiting for CVOX result"); + } return false; } try { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Start waiting..."); + } mResultLock.wait(waitTimeMillis); } catch (InterruptedException ie) { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Interrupted while waiting for CVOX result"); + } } } } @@ -878,8 +921,9 @@ class AccessibilityInjector { @JavascriptInterface @SuppressWarnings("unused") public void onResult(String id, String result) { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Saw CVOX result of '" + result + "' for ID " + id); + } final int resultId; try { @@ -893,11 +937,34 @@ class AccessibilityInjector { mResult = Boolean.parseBoolean(result); mResultId = resultId; } else { - if (DEBUG) + if (DEBUG) { Log.w(TAG, "Result with ID " + resultId + " was stale vesus " + mResultId); + } } mResultLock.notifyAll(); } } + + /** + * Requests a callback to ensure that the JavaScript interface for this + * object has been added successfully. + * + * @param webView The web view to request a callback from. + * @param callbackRunnable Runnable to execute if a callback is received. + */ + public void requestCallback(WebView webView, Runnable callbackRunnable) { + mCallbackRunnable = callbackRunnable; + + webView.loadUrl("javascript:(function() { " + mInterfaceName + ".callback(); })();"); + } + + @JavascriptInterface + @SuppressWarnings("unused") + public void callback() { + if (mCallbackRunnable != null) { + mMainHandler.post(mCallbackRunnable); + mCallbackRunnable = null; + } + } } } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 0f8966e..ae56e6b 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -2500,6 +2500,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // Remove all pending messages because we are restoring previous // state. mWebViewCore.removeMessages(); + if (isAccessibilityInjectionEnabled()) { + getAccessibilityInjector().addAccessibilityApisIfNecessary(); + } // Send a restore state message. mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index); } diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java index 3e9107f..c6b6dd6 100644 --- a/core/java/android/widget/DigitalClock.java +++ b/core/java/android/widget/DigitalClock.java @@ -17,7 +17,6 @@ package android.widget; import android.content.Context; -import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.SystemClock; @@ -32,14 +31,12 @@ import java.util.Calendar; /** * Like AnalogClock, but digital. Shows seconds. * - * FIXME: implement separate views for hours/minutes/seconds, so - * proportional fonts don't shake rendering - * - * @deprecated It is recommended you use a {@link TextView} and {@link DateFormat} - * to implement the same behavior. + * @deprecated It is recommended you use {@link TextClock} instead. */ @Deprecated public class DigitalClock extends TextView { + // FIXME: implement separate views for hours/minutes/seconds, so + // proportional fonts don't shake rendering Calendar mCalendar; private final static String m12 = "h:mm:ss aa"; @@ -86,16 +83,16 @@ public class DigitalClock extends TextView { * requests a tick on the next hard-second boundary */ mTicker = new Runnable() { - public void run() { - if (mTickerStopped) return; - mCalendar.setTimeInMillis(System.currentTimeMillis()); - setText(DateFormat.format(mFormat, mCalendar)); - invalidate(); - long now = SystemClock.uptimeMillis(); - long next = now + (1000 - now % 1000); - mHandler.postAtTime(mTicker, next); - } - }; + public void run() { + if (mTickerStopped) return; + mCalendar.setTimeInMillis(System.currentTimeMillis()); + setText(DateFormat.format(mFormat, mCalendar)); + invalidate(); + long now = SystemClock.uptimeMillis(); + long next = now + (1000 - now % 1000); + mHandler.postAtTime(mTicker, next); + } + }; mTicker.run(); } @@ -134,12 +131,14 @@ public class DigitalClock extends TextView { @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); + //noinspection deprecation event.setClassName(DigitalClock.class.getName()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); + //noinspection deprecation info.setClassName(DigitalClock.class.getName()); } } diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java new file mode 100644 index 0000000..4c46658 --- /dev/null +++ b/core/java/android/widget/TextClock.java @@ -0,0 +1,482 @@ +/* + * 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.widget; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.TypedArray; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.SystemClock; +import android.provider.Settings; +import android.text.format.DateFormat; +import android.util.AttributeSet; + +import com.android.internal.R; + +import java.util.Calendar; +import java.util.TimeZone; + +import static android.view.ViewDebug.ExportedProperty; +import static android.widget.RemoteViews.*; + +/** + * <p><code>TextClock</code> can display the current date and/or time as + * a formatted string.</p> + * + * <p>This view honors the 24-hour format system setting. As such, it is + * possible and recommended to provide two different formatting patterns: + * one to display the date/time in 24-hour mode and one to display the + * date/time in 12-hour mode.</p> + * + * <p>It is possible to determine whether the system is currently in + * 24-hour mode by calling {@link #is24HourModeEnabled()}.</p> + * + * <p>The rules used by this widget to decide how to format the date and + * time are the following:</p> + * <ul> + * <li>In 24-hour mode: + * <ul> + * <li>Use the value returned by {@link #getFormat24Hour()} when non-null</li> + * <li>Otherwise, use the value returned by {@link #getFormat12Hour()} when non-null</li> + * <li>Otherwise, use {@link #DEFAULT_FORMAT_24_HOUR}</li> + * </ul> + * </li> + * <li>In 12-hour mode: + * <ul> + * <li>Use the value returned by {@link #getFormat12Hour()} when non-null</li> + * <li>Otherwise, use the value returned by {@link #getFormat24Hour()} when non-null</li> + * <li>Otherwise, use {@link #DEFAULT_FORMAT_12_HOUR}</li> + * </ul> + * </li> + * </ul> + * + * <p>The {@link CharSequence} instances used as formatting patterns when calling either + * {@link #setFormat24Hour(CharSequence)} or {@link #setFormat12Hour(CharSequence)} can + * contain styling information. To do so, use a {@link android.text.Spanned} object.</p> + * + * @attr ref android.R.styleable#TextClock_format12Hour + * @attr ref android.R.styleable#TextClock_format24Hour + * @attr ref android.R.styleable#TextClock_timeZone + */ +@RemoteView +public class TextClock extends TextView { + /** + * The default formatting pattern in 12-hour mode. This pattenr is used + * if {@link #setFormat12Hour(CharSequence)} is called with a null pattern + * or if no pattern was specified when creating an instance of this class. + * + * This default pattern shows only the time, hours and minutes, and an am/pm + * indicator. + * + * @see #setFormat12Hour(CharSequence) + * @see #getFormat12Hour() + */ + public static final CharSequence DEFAULT_FORMAT_12_HOUR = "h:mm aa"; + + /** + * The default formatting pattern in 24-hour mode. This pattenr is used + * if {@link #setFormat24Hour(CharSequence)} is called with a null pattern + * or if no pattern was specified when creating an instance of this class. + * + * This default pattern shows only the time, hours and minutes. + * + * @see #setFormat24Hour(CharSequence) + * @see #getFormat24Hour() + */ + public static final CharSequence DEFAULT_FORMAT_24_HOUR = "k:mm"; + + private CharSequence mFormat12 = DEFAULT_FORMAT_12_HOUR; + private CharSequence mFormat24 = DEFAULT_FORMAT_24_HOUR; + + @ExportedProperty + private CharSequence mFormat; + @ExportedProperty + private boolean mHasSeconds; + + private boolean mAttached; + + private Calendar mTime; + private String mTimeZone; + + private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + chooseFormat(); + onTimeChanged(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + chooseFormat(); + onTimeChanged(); + } + }; + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mTimeZone == null) { + if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { + final String timeZone = intent.getStringExtra("time-zone"); + createTime(timeZone); + } + onTimeChanged(); + } + } + }; + + private final Runnable mTicker = new Runnable() { + public void run() { + onTimeChanged(); + + long now = SystemClock.uptimeMillis(); + long next = now + (1000 - now % 1000); + + getHandler().postAtTime(mTicker, next); + } + }; + + /** + * Creates a new clock using the default patterns + * {@link #DEFAULT_FORMAT_24_HOUR} and {@link #DEFAULT_FORMAT_12_HOUR} + * respectively for the 24-hour and 12-hour modes. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + */ + @SuppressWarnings("UnusedDeclaration") + public TextClock(Context context) { + super(context); + init(); + } + + /** + * Creates a new clock inflated from XML. This object's properties are + * intialized from the attributes specified in XML. + * + * This constructor uses a default style of 0, so the only attribute values + * applied are those in the Context's Theme and the given AttributeSet. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param attrs The attributes of the XML tag that is inflating the view + */ + @SuppressWarnings("UnusedDeclaration") + public TextClock(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Creates a new clock inflated from XML. This object's properties are + * intialized from the attributes specified in XML. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param attrs The attributes of the XML tag that is inflating the view + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource + */ + public TextClock(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextClock, defStyle, 0); + try { + CharSequence format; + + format = a.getText(R.styleable.TextClock_format12Hour); + mFormat12 = format == null ? DEFAULT_FORMAT_12_HOUR : format; + + format = a.getText(R.styleable.TextClock_format24Hour); + mFormat24 = format == null ? DEFAULT_FORMAT_24_HOUR : format; + + mTimeZone = a.getString(R.styleable.TextClock_timeZone); + } finally { + a.recycle(); + } + + init(); + } + + private void init() { + createTime(mTimeZone); + // Wait until onAttachedToWindow() to handle the ticker + chooseFormat(false); + } + + private void createTime(String timeZone) { + if (timeZone != null) { + mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone)); + } else { + mTime = Calendar.getInstance(); + } + } + + /** + * Returns the formatting pattern used to display the date and/or time + * in 12-hour mode. The formatting pattern syntax is described in + * {@link DateFormat}. + * + * @return A {@link CharSequence} or null. + * + * @see #setFormat12Hour(CharSequence) + * @see #is24HourModeEnabled() + */ + @ExportedProperty + public CharSequence getFormat12Hour() { + return mFormat12; + } + + /** + * Specifies the formatting pattern used to display the date and/or time + * in 12-hour mode. The formatting pattern syntax is described in + * {@link DateFormat}. + * + * If this pattern is set to null, {@link #getFormat24Hour()} will be used + * even in 12-hour mode. If both 24-hour and 12-hour formatting patterns + * are set to null, {@link #DEFAULT_FORMAT_24_HOUR} and + * {@link #DEFAULT_FORMAT_12_HOUR} will be used instead. + * + * @param format A date/time formatting pattern as described in {@link DateFormat} + * + * @see #getFormat12Hour() + * @see #is24HourModeEnabled() + * @see #DEFAULT_FORMAT_12_HOUR + * @see DateFormat + * + * @attr ref android.R.styleable#TextClock_format12Hour + */ + public void setFormat12Hour(CharSequence format) { + mFormat12 = format; + + chooseFormat(); + onTimeChanged(); + } + + /** + * Returns the formatting pattern used to display the date and/or time + * in 24-hour mode. The formatting pattern syntax is described in + * {@link DateFormat}. + * + * @return A {@link CharSequence} or null. + * + * @see #setFormat24Hour(CharSequence) + * @see #is24HourModeEnabled() + */ + @ExportedProperty + public CharSequence getFormat24Hour() { + return mFormat24; + } + + /** + * Specifies the formatting pattern used to display the date and/or time + * in 24-hour mode. The formatting pattern syntax is described in + * {@link DateFormat}. + * + * If this pattern is set to null, {@link #getFormat12Hour()} will be used + * even in 24-hour mode. If both 24-hour and 12-hour formatting patterns + * are set to null, {@link #DEFAULT_FORMAT_24_HOUR} and + * {@link #DEFAULT_FORMAT_12_HOUR} will be used instead. + * + * @param format A date/time formatting pattern as described in {@link DateFormat} + * + * @see #getFormat24Hour() + * @see #is24HourModeEnabled() + * @see #DEFAULT_FORMAT_24_HOUR + * @see DateFormat + * + * @attr ref android.R.styleable#TextClock_format24Hour + */ + public void setFormat24Hour(CharSequence format) { + mFormat24 = format; + + chooseFormat(); + onTimeChanged(); + } + + /** + * Indicates whether the system is currently using the 24-hour mode. + * + * When the system is in 24-hour mode, this view will use the pattern + * returned by {@link #getFormat24Hour()}. In 12-hour mode, the pattern + * returned by {@link #getFormat12Hour()} is used instead. + * + * If either one of the formats is null, the other format is used. If + * both formats are null, the default values {@link #DEFAULT_FORMAT_12_HOUR} + * and {@link #DEFAULT_FORMAT_24_HOUR} are used instead. + * + * @return true if time should be displayed in 24-hour format, false if it + * should be displayed in 12-hour format. + * + * @see #setFormat12Hour(CharSequence) + * @see #getFormat12Hour() + * @see #setFormat24Hour(CharSequence) + * @see #getFormat24Hour() + */ + public boolean is24HourModeEnabled() { + return DateFormat.is24HourFormat(getContext()); + } + + /** + * Indicates which time zone is currently used by this view. + * + * @return The ID of the current time zone or null if the default time zone, + * as set by the user, must be used + * + * @see TimeZone + * @see java.util.TimeZone#getAvailableIDs() + * @see #setTimeZone(String) + */ + public String getTimeZone() { + return mTimeZone; + } + + /** + * Sets the specified time zone to use in this clock. When the time zone + * is set through this method, system time zone changes (when the user + * sets the time zone in settings for instance) will be ignored. + * + * @param timeZone The desired time zone's ID as specified in {@link TimeZone} + * or null to user the time zone specified by the user + * (system time zone) + * + * @see #getTimeZone() + * @see java.util.TimeZone#getAvailableIDs() + * @see TimeZone#getTimeZone(String) + * + * @attr ref android.R.styleable#TextClock_timeZone + */ + public void setTimeZone(String timeZone) { + mTimeZone = timeZone; + + createTime(timeZone); + onTimeChanged(); + } + + /** + * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} + * depending on whether the user has selected 24-hour format. + * + * Calling this method does not schedule or unschedule the time ticker. + */ + private void chooseFormat() { + chooseFormat(true); + } + + /** + * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} + * depending on whether the user has selected 24-hour format. + * + * @param handleTicker true if calling this method should schedule/unschedule the + * time ticker, false otherwise + */ + private void chooseFormat(boolean handleTicker) { + final boolean format24Requested = is24HourModeEnabled(); + + if (format24Requested) { + mFormat = abc(mFormat24, mFormat12, DEFAULT_FORMAT_24_HOUR); + } else { + mFormat = abc(mFormat12, mFormat24, DEFAULT_FORMAT_12_HOUR); + } + + boolean hadSeconds = mHasSeconds; + mHasSeconds = DateFormat.hasSeconds(mFormat); + + if (handleTicker) { + if (hadSeconds != mHasSeconds) { + if (hadSeconds) getHandler().removeCallbacks(mTicker); + else mTicker.run(); + } + } + } + + /** + * Returns a if not null, else return b if not null, else return c. + */ + private static CharSequence abc(CharSequence a, CharSequence b, CharSequence c) { + return a == null ? (b == null ? c : b) : a; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (!mAttached) { + mAttached = true; + + registerReceiver(); + registerObserver(); + + createTime(mTimeZone); + + if (mHasSeconds) { + mTicker.run(); + } else { + onTimeChanged(); + } + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAttached) { + unregisterReceiver(); + unregisterObserver(); + + getHandler().removeCallbacks(mTicker); + + mAttached = false; + } + } + + private void registerReceiver() { + final IntentFilter filter = new IntentFilter(); + + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + + getContext().registerReceiver(mIntentReceiver, filter, null, getHandler()); + } + + private void registerObserver() { + final ContentResolver resolver = getContext().getContentResolver(); + resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver); + } + + private void unregisterReceiver() { + getContext().unregisterReceiver(mIntentReceiver); + } + + private void unregisterObserver() { + final ContentResolver resolver = getContext().getContentResolver(); + resolver.unregisterContentObserver(mFormatChangeObserver); + } + + private void onTimeChanged() { + mTime.setTimeInMillis(System.currentTimeMillis()); + setText(DateFormat.format(mFormat, mTime)); + } +} diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl index a017722..017801b 100644 --- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl +++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl @@ -23,7 +23,6 @@ interface IFaceLockInterface { void startUi(IBinder containingWindowToken, int x, int y, int width, int height, boolean useLiveliness); void stopUi(); - void makeInvisible(); void registerCallback(IFaceLockCallback cb); void unregisterCallback(IFaceLockCallback cb); } diff --git a/core/java/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java new file mode 100644 index 0000000..e3c1247 --- /dev/null +++ b/core/java/com/android/internal/widget/FaceUnlockView.java @@ -0,0 +1,69 @@ +/* + * 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.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.RelativeLayout; + +public class FaceUnlockView extends RelativeLayout { + private static final String TAG = "FaceUnlockView"; + + public FaceUnlockView(Context context) { + this(context, null); + } + + public FaceUnlockView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private int resolveMeasured(int measureSpec, int desired) + { + int result = 0; + int specSize = MeasureSpec.getSize(measureSpec); + switch (MeasureSpec.getMode(measureSpec)) { + case MeasureSpec.UNSPECIFIED: + result = desired; + break; + case MeasureSpec.AT_MOST: + result = Math.max(specSize, desired); + break; + case MeasureSpec.EXACTLY: + default: + result = specSize; + } + return result; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int minimumWidth = getSuggestedMinimumWidth(); + final int minimumHeight = getSuggestedMinimumHeight(); + int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth); + int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight); + + final int chosenSize = Math.min(viewWidth, viewHeight); + final int newWidthMeasureSpec = + MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST); + final int newHeightMeasureSpec = + MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST); + + super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); + } +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f8c3b4d..3ee3c8c 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -129,6 +129,11 @@ public class LockPatternUtils { */ public static final int ID_DEFAULT_STATUS_WIDGET = -2; + /** + * Intent extra that's used to tag the default widget when using the picker + */ + public static final String EXTRA_DEFAULT_WIDGET = "com.android.settings.DEFAULT_WIDGET"; + 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"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 92aa06a..3f20ed1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -503,14 +503,16 @@ android:permissionGroupFlags="personalInfo" android:priority="330" /> - <!-- Allows an application to access fine (e.g., GPS) location --> + <!-- Allows an app to access precise location from location sources such + as GPS, cell towers, and Wi-Fi. --> <permission android:name="android.permission.ACCESS_FINE_LOCATION" android:permissionGroup="android.permission-group.LOCATION" android:protectionLevel="dangerous" android:label="@string/permlab_accessFineLocation" android:description="@string/permdesc_accessFineLocation" /> - <!-- Allows an application to access coarse (e.g., Cell-ID, WiFi) location --> + <!-- Allows an app to access approximate location derived from network location + sources such as cell towers and Wi-Fi. --> <permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:permissionGroup="android.permission-group.LOCATION" android:protectionLevel="dangerous" @@ -1901,6 +1903,13 @@ android:description="@string/permdesc_bindGadget" android:protectionLevel="signature|system" /> + <!-- Private permission, to restrict who can bring up a dialog to add a new + keyguard widget + @hide --> + <permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:protectionLevel="signature|system" /> + <!-- Internal permission allowing an application to query/set which applications can bind AppWidgets. @hide --> diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml index bb455bd..67ac1d5 100644 --- a/core/res/res/layout-land/keyguard_host_view.xml +++ b/core/res/res/layout-land/keyguard_host_view.xml @@ -50,14 +50,14 @@ <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@dimen/keyguard_security_width" + android:layout_height="@dimen/keyguard_security_height" 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:layout_width="match_parent" + android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:paddingLeft="@dimen/keyguard_security_view_margin" diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml index ed55e61..a3c8105 100644 --- a/core/res/res/layout-port/keyguard_host_view.xml +++ b/core/res/res/layout-port/keyguard_host_view.xml @@ -36,7 +36,8 @@ <FrameLayout android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + androidprv:layout_childType="widgets"> <include layout="@layout/keyguard_widget_pager" android:id="@+id/app_widget_container" android:layout_width="match_parent" @@ -52,21 +53,18 @@ <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="@dimen/keyguard_security_height" 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:padding="0dp" 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:layout_width="match_parent" + android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - android:paddingLeft="@dimen/keyguard_security_view_margin" android:paddingTop="@dimen/keyguard_security_view_margin" - android:paddingRight="@dimen/keyguard_security_view_margin" android:gravity="center"> </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper> </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer> diff --git a/core/res/res/layout-port/keyguard_widget_pager.xml b/core/res/res/layout-port/keyguard_widget_pager.xml index 3b29db8..7fd370b 100644 --- a/core/res/res/layout-port/keyguard_widget_pager.xml +++ b/core/res/res/layout-port/keyguard_widget_pager.xml @@ -25,7 +25,7 @@ android:paddingLeft="25dp" android:paddingRight="25dp" android:paddingTop="25dp" - android:paddingBottom="64dp" + android:paddingBottom="@dimen/kg_widget_pager_bottom_padding" android:clipChildren="false" android:clipToPadding="false" androidprv:pageSpacing="10dp"> diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml index ae7984c..976d0c6 100644 --- a/core/res/res/layout/keyguard_face_unlock_view.xml +++ b/core/res/res/layout/keyguard_face_unlock_view.xml @@ -30,10 +30,10 @@ android:layout_height="wrap_content" /> - <RelativeLayout + <com.android.internal.widget.FaceUnlockView android:id="@+id/face_unlock_area_view" android:layout_width="match_parent" - android:layout_height="@*android:dimen/face_unlock_height" + android:layout_height="0dp" android:background="@*android:drawable/intro_bg" android:gravity="center" android:layout_weight="1"> @@ -55,8 +55,7 @@ android:background="#00000000" android:src="@*android:drawable/ic_facial_backup" /> - - </RelativeLayout> + </com.android.internal.widget.FaceUnlockView> <include layout="@layout/keyguard_emergency_carrier_area" android:id="@+id/keyguard_selector_fade_container" diff --git a/core/res/res/layout/keyguard_password_view.xml b/core/res/res/layout/keyguard_password_view.xml index a184415..b6faf5b 100644 --- a/core/res/res/layout/keyguard_password_view.xml +++ b/core/res/res/layout/keyguard_password_view.xml @@ -22,73 +22,61 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_gravity="center"> + android:gravity="bottom" + > - <FrameLayout + <Space android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1"> + android:layout_weight="1" + /> - <include layout="@layout/keyguard_message_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - /> - - <LinearLayout - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:orientation="vertical" - android:layout_gravity="center"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> + <include layout="@layout/keyguard_message_area" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> - <!-- Password entry field --> - <!-- Note: the entire container is styled to look like the edit field, - since the backspace/IME switcher looks better inside --> - <LinearLayout - android:layout_gravity="center_vertical|fill_horizontal" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:orientation="horizontal" - android:background="#70000000" - android:layout_marginStart="4dip" - android:layout_marginEnd="4dip"> + <!-- Password entry field --> + <!-- Note: the entire container is styled to look like the edit field, + since the backspace/IME switcher looks better inside --> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:orientation="horizontal" + android:background="#70000000" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + > - <EditText android:id="@+id/passwordEntry" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:gravity="center_horizontal" - android:layout_gravity="center_vertical" - android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="36sp" - android:background="@null" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|actionDone" - /> + <EditText android:id="@+id/passwordEntry" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center_horizontal" + android:layout_gravity="center_vertical" + android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" + android:singleLine="true" + android:textStyle="normal" + android:inputType="textPassword" + android:textSize="36sp" + android:background="@null" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="#ffffffff" + android:imeOptions="flagForceAscii|actionDone" + /> - <ImageView android:id="@+id/switch_ime_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@*android:drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" - /> + <ImageView android:id="@+id/switch_ime_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@*android:drawable/ic_lockscreen_ime" + android:clickable="true" + android:padding="8dip" + android:layout_gravity="center" + android:background="?android:attr/selectableItemBackground" + android:visibility="gone" + /> - </LinearLayout> - </LinearLayout> </LinearLayout> - </FrameLayout> + <include layout="@layout/keyguard_emergency_carrier_area" android:id="@+id/keyguard_selector_fade_container" android:layout_width="match_parent" diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml index d62570b..2529196 100644 --- a/core/res/res/layout/keyguard_pin_view.xml +++ b/core/res/res/layout/keyguard_pin_view.xml @@ -21,8 +21,8 @@ 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:layout_width="match_parent" + android:layout_height="match_parent" android:orientation="vertical" > <include layout="@layout/keyguard_message_area" @@ -35,7 +35,7 @@ android:orientation="horizontal" android:layout_weight="1" > - <TextView android:id="@+id/passwordEntry" + <TextView android:id="@+id/pinEntry" android:editable="true" android:layout_width="0dip" android:layout_height="match_parent" @@ -59,6 +59,7 @@ android:paddingLeft="24dp" android:paddingRight="24dp" android:background="?android:attr/selectableItemBackground" + android:contentDescription="@string/keyboardview_keycode_delete" /> </LinearLayout> <View @@ -78,7 +79,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="1" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -87,7 +88,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="2" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -96,7 +97,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="3" /> </LinearLayout> @@ -112,7 +113,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="4" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -121,7 +122,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="5" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -130,7 +131,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="6" /> </LinearLayout> @@ -146,7 +147,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="7" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -155,7 +156,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="8" /> <view class="com.android.internal.policy.impl.keyguard.NumPadKey" @@ -164,7 +165,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="9" /> </LinearLayout> @@ -185,7 +186,7 @@ android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" - androidprv:textView="@+id/passwordEntry" + androidprv:textView="@+id/pinEntry" androidprv:digit="0" /> <ImageButton @@ -196,6 +197,7 @@ android:layout_height="match_parent" android:layout_weight="1" android:src="@drawable/sym_keyboard_return_holo" + android:contentDescription="@string/keyboardview_keycode_enter" /> </LinearLayout> diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 07f62ed..36f2628 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -50,4 +50,10 @@ <!-- Space reserved at the bottom of secure views (pin/pattern/password/SIM pin/SIM puk) --> <dimen name="kg_secure_padding_height">0dp</dimen> + <!-- Top padding for the widget pager --> + <dimen name="kg_widget_pager_top_padding">0dp</dimen> + + <!-- Bottom padding for the widget pager --> + <dimen name="kg_widget_pager_bottom_padding">0dp</dimen> + </resources> diff --git a/core/res/res/values-sw380dp/dimens.xml b/core/res/res/values-sw380dp/dimens.xml new file mode 100644 index 0000000..fc0e85d --- /dev/null +++ b/core/res/res/values-sw380dp/dimens.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) --> + <dimen name="keyguard_security_width">340dp</dimen> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 0d01df4..52c230b 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -98,10 +98,10 @@ <dimen name="kg_widget_pager_horizontal_padding">24dp</dimen> <!-- Top padding for the widget pager --> - <dimen name="kg_widget_pager_top_padding">24dp</dimen> + <dimen name="kg_widget_pager_top_padding">0dp</dimen> <!-- Bottom padding for the widget pager --> - <dimen name="kg_widget_pager_bottom_padding">16dp</dimen> + <dimen name="kg_widget_pager_bottom_padding">0dp</dimen> <!-- Top margin for the runway lights. We add a negative margin in large devices to account for the widget pager padding --> diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml index d6d2b66..ccdb4be 100644 --- a/core/res/res/values-sw720dp/dimens.xml +++ b/core/res/res/values-sw720dp/dimens.xml @@ -91,10 +91,10 @@ <dimen name="kg_widget_pager_horizontal_padding">80dp</dimen> <!-- Top padding for the widget pager --> - <dimen name="kg_widget_pager_top_padding">32dp</dimen> + <dimen name="kg_widget_pager_top_padding">0dp</dimen> <!-- Bottom padding for the widget pager --> - <dimen name="kg_widget_pager_bottom_padding">36dp</dimen> + <dimen name="kg_widget_pager_bottom_padding">0dp</dimen> <!-- Top margin for the runway lights. We add a negative margin in large devices to account for the widget pager padding --> @@ -112,4 +112,9 @@ <!-- Size of the text under the avator on the multiuser lockscreen. --> <dimen name="keyguard_avatar_name_size">12sp</dimen> + <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) --> + <dimen name="keyguard_security_width">420dp</dimen> + + <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) --> + <dimen name="keyguard_security_height">420dp</dimen> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d186c4a..186c8b3 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3046,6 +3046,24 @@ <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> <attr name="textAllCaps" format="boolean" /> </declare-styleable> + <declare-styleable name="TextClock"> + <!-- Specifies the formatting pattern used to show the time and/or date + in 12-hour mode. Please refer to {@link android.text.format.DateFormat} + for a complete description of accepted formatting patterns. + The default pattern is "h:mm aa". --> + <attr name="format12Hour" format="string"/> + <!-- Specifies the formatting pattern used to show the time and/or date + in 24-hour mode. Please refer to {@link android.text.format.DateFormat} + for a complete description of accepted formatting patterns. + The default pattern is "k:mm". --> + <attr name="format24Hour" format="string"/> + <!-- Specifies the time zone to use. When this attribute is specified, the + TextClock will ignore the time zone of the system. To use the user's + time zone, do not specify this attribute. The default value is the + user's time zone. Please refer to {@link java.util.TimeZone} for more + information about time zone ids. --> + <attr name="timeZone" format="string"/> + </declare-styleable> <declare-styleable name="TextSwitcher"> </declare-styleable> <declare-styleable name="TextView"> @@ -5798,6 +5816,8 @@ <!-- Scrim. This will block access to child views that come before it in the child list in bouncer mode. --> <enum name="scrim" value="4" /> + <!-- The home for widgets. All widgets will be descendents of this. --> + <enum name="widgets" value="5" /> </attr> <declare-styleable name="SlidingChallengeLayout"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0890a18..148560a 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -650,9 +650,9 @@ speech --> <bool name="config_bluetooth_wide_band_speech">true</bool> - <!-- Boolean indicating if current platform supports quick switch-on/off of - Bluetooth Module --> - <bool name="config_bluetooth_adapter_quick_switch">true</bool> + <!-- Boolean indicating if current platform need do one-time bluetooth address + re-validation --> + <bool name="config_bluetooth_address_validation">false</bool> <!-- The default data-use polling period. --> <integer name="config_datause_polling_period_sec">600</integer> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b830e79..4966b97 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -294,7 +294,7 @@ <dimen name="kg_widget_pager_top_padding">0dp</dimen> <!-- Bottom padding for the widget pager --> - <dimen name="kg_widget_pager_bottom_padding">0dp</dimen> + <dimen name="kg_widget_pager_bottom_padding">64dp</dimen> <!-- Top margin for the runway lights. We add a negative margin in large devices to account for the widget pager padding --> @@ -303,6 +303,12 @@ <!-- Touch slop for the global toggle accessibility gesture --> <dimen name="accessibility_touch_slop">80dip</dimen> + <!-- Width of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) --> + <dimen name="keyguard_security_width">320dp</dimen> + + <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) --> + <dimen name="keyguard_security_height">400dp</dimen> + <!-- Margin around the various security views --> <dimen name="keyguard_security_view_margin">8dp</dimen> diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml index d1df2a4..053fc85 100644 --- a/core/res/res/values/integers.xml +++ b/core/res/res/values/integers.xml @@ -18,7 +18,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> + <integer name="kg_security_flip_duration">100</integer> + <integer name="kg_security_fade_duration">100</integer> <integer name="kg_glowpad_rotation_offset">0</integer> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index d7a480b..06d3110 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2020,6 +2020,9 @@ <public type="attr" name="permissionFlags" id="0x010103c7" /> <public type="attr" name="checkedTextViewStyle" id="0x010103c8" /> <public type="attr" name="showOnLockScreen" id="0x010103c9" /> + <public type="attr" name="format12Hour" id="0x010103ca" /> + <public type="attr" name="format24Hour" id="0x010103cb" /> + <public type="attr" name="timeZone" id="0x010103cc" /> <public type="style" name="Widget.Holo.CheckedTextView" id="0x010301d9" /> <public type="style" name="Widget.Holo.Light.CheckedTextView" id="0x010301da" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 159eec1..51d6429 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2065,6 +2065,15 @@ <!-- This can be used in any application wanting to disable the text "Emergency number" --> <string name="emergency_call_dialog_number_for_display">Emergency number</string> + <!-- String to display if the clock status widget is selected (it is the default) [CHAR LIMIT=22] --> + <string name="widget_default" msgid="8269383575996003796">Clock</string> + + <!-- Package name for default widget [DO NOT TRANSLATE] --> + <string name="widget_default_package_name">com.android.deskclock</string> + + <!-- Class name for default widget [DO NOT TRANSLATE] --> + <string name="widget_default_class_name">com.android.deskclock.DeskClock</string> + <!-- *** touch based lock / unlock *** --> <skip /> @@ -2276,14 +2285,12 @@ <string name="keyguard_accessibility_add_widget">Add widget.</string> <!-- Accessibility description of the empty sidget slot (place holder for a new widget). [CHAR_LIMIT=none] --> <string name="keyguard_accessibility_widget_empty_slot">Empty</string> - <!-- Accessibility description of the unlock area. [CHAR_LIMIT=none] --> - <string name="keyguard_accessibility_unlock_area">Unlock area.</string> <!-- Accessibility description of the event of expanding an unlock area. [CHAR_LIMIT=none] --> <string name="keyguard_accessibility_unlock_area_expanded">Unlock area expanded.</string> <!-- Accessibility description of the event of collapsing an unlock area. [CHAR_LIMIT=none] --> <string name="keyguard_accessibility_unlock_area_collapsed">Unlock area collapsed.</string> <!-- Accessibility description of a lock screen widget. [CHAR_LIMIT=none] --> - <string name="keyguard_accessibility_widget">%1$s widget.</string> + <string name="keyguard_accessibility_widget"><xliff:g id="widget_index">%1$s</xliff:g> widget.</string> <!-- Accessibility description of the lock screen user selector widget. [CHAR_LIMIT=none] --> <string name="keyguard_accessibility_user_selector">User selector</string> <!-- Accessibility description of the lock screen status widget. [CHAR_LIMIT=none] --> @@ -2292,6 +2299,28 @@ <string name="keyguard_accessibility_camera">Camera</string> <!-- Accessibility description of the lock media control widget. [CHAR_LIMIT=none] --> <string name="keygaurd_accessibility_media_controls">Media controls</string> + <!-- Accessibility description of widget reordering start. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_widget_reorder_start">Widget reordering started.</string> + <!-- Accessibility description of widget reordering end. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_widget_reorder_end">Widget reordering ended.</string> + <!-- Accessibility description of the a widget deletion event. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_widget_deleted">Widget <xliff:g id="widget_index">%1$s</xliff:g> deleted.</string> + <!-- Accessibility description of the button to expand the lock area. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_expand_lock_area">Expand unlock area.</string> + <!-- Accessibility description of the slide unlock. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_slide_unlock">Slide unlock.</string> + <!-- Accessibility description of the pattern unlock. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_pattern_unlock">Pattern unlock.</string> + <!-- Accessibility description of the face unlock. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_face_unlock">Face unlock.</string> + <!-- Accessibility description of the pin lock. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_pin_unlock">Pin unlock.</string> + <!-- Accessibility description of the password lock. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_password_unlock">Password unlock.</string> + <!-- Accessibility description of the unlock pattern area. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_pattern_area">Pattern area.</string> + <!-- Accessibility description of the unlock slide area. [CHAR_LIMIT=none] --> + <string name="keyguard_accessibility_slide_area">Slide area.</string> <!-- Password keyboard strings. Used by LockScreen and Settings --><skip /> <!-- Label for "switch to symbols" key. Must be short to fit on key! --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7ebf7e7..e820ace 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -245,7 +245,7 @@ <java-symbol type="bool" name="action_bar_embed_tabs_pre_jb" /> <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> <java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" /> - <java-symbol type="bool" name="config_bluetooth_adapter_quick_switch" /> + <java-symbol type="bool" name="config_bluetooth_address_validation" /> <java-symbol type="bool" name="config_bluetooth_sco_off_call" /> <java-symbol type="bool" name="config_cellBroadcastAppLinks" /> <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" /> @@ -479,6 +479,9 @@ <java-symbol type="string" name="emailTypeOther" /> <java-symbol type="string" name="emailTypeWork" /> <java-symbol type="string" name="emergency_call_dialog_number_for_display" /> + <java-symbol type="string" name="widget_default" /> + <java-symbol type="string" name="widget_default_package_name" /> + <java-symbol type="string" name="widget_default_class_name" /> <java-symbol type="string" name="emergency_calls_only" /> <java-symbol type="string" name="enable_accessibility_canceled" /> <java-symbol type="string" name="eventTypeAnniversary" /> @@ -1269,6 +1272,7 @@ <java-symbol type="id" name="option3" /> <java-symbol type="id" name="password" /> <java-symbol type="id" name="passwordEntry" /> + <java-symbol type="id" name="pinEntry" /> <java-symbol type="id" name="pinDel" /> <java-symbol type="id" name="pinDisplay" /> <java-symbol type="id" name="owner_info" /> diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index 1bbc7df..5db7ffc 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -74,4 +74,5 @@ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.INJECT_EVENTS" /> </manifest> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index 561e33e..245f537 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -65,6 +65,8 @@ public class ConnectivityManagerTestActivity extends Activity { public static final long LONG_TIMEOUT = 50 * 1000; // 2 minutes timer between wifi stop and start public static final long WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; + // Set ping test timer to be 3 minutes + public static final long PING_TIMER = 3 * 60 *1000; public static final int SUCCESS = 0; // for Wifi tethering state change public static final int FAILURE = 1; public static final int INIT = -1; @@ -517,37 +519,36 @@ public class ConnectivityManagerTestActivity extends Activity { * @return true if the ping test is successful, false otherwise. */ public boolean pingTest(String[] pingServerList) { - boolean result = false; String[] hostList = {"www.google.com", "www.yahoo.com", "www.bing.com", "www.facebook.com", "www.ask.com"}; if (pingServerList != null) { hostList = pingServerList; } - try { - // assume the chance that all servers are down is very small - for (int i = 0; i < hostList.length; i++ ) { - String host = hostList[i]; - log("Start ping test, ping " + host); - Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host); - int status = p.waitFor(); - if (status == 0) { - // if any of the ping test is successful, return true - result = true; - break; - } else { - result = false; - log("ping " + host + " failed."); + + long startTime = System.currentTimeMillis(); + while ((System.currentTimeMillis() - startTime) < PING_TIMER) { + try { + // assume the chance that all servers are down is very small + for (int i = 0; i < hostList.length; i++ ) { + String host = hostList[i]; + log("Start ping test, ping " + host); + Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host); + int status = p.waitFor(); + if (status == 0) { + // if any of the ping test is successful, return true + return true; + } } + } catch (UnknownHostException e) { + log("Ping test Fail: Unknown Host"); + } catch (IOException e) { + log("Ping test Fail: IOException"); + } catch (InterruptedException e) { + log("Ping test Fail: InterruptedException"); } - } catch (UnknownHostException e) { - log("Ping test Fail: Unknown Host"); - } catch (IOException e) { - log("Ping test Fail: IOException"); - } catch (InterruptedException e) { - log("Ping test Fail: InterruptedException"); } - log("return"); - return result; + // ping test timeout + return false; } /** diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java index 79d928c..7bfb594 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java @@ -28,6 +28,7 @@ import android.net.wifi.WifiManager; import android.os.Environment; import android.os.PowerManager; import android.provider.Settings; +import android.view.KeyEvent; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -289,6 +290,11 @@ public class WifiStressTest // Turn screen on again mAct.turnScreenOn(); + // Wait for 2 seconds for the lock screen + sleep(2 * 1000, "wait 2 seconds for lock screen"); + // Disable lock screen by inject menu key event + mRunner.sendKeyDownUpSync(KeyEvent.KEYCODE_MENU); + // Measure the time for Wi-Fi to get connected long startTime = System.currentTimeMillis(); assertTrue("Wait for Wi-Fi enable timeout after wake up", diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd index 463343d..a3538a9 100644 --- a/docs/html/distribute/googleplay/publish/preparing.jd +++ b/docs/html/distribute/googleplay/publish/preparing.jd @@ -15,7 +15,7 @@ page.title=Publishing Checklist for Google Play <li><a href="#inapp-billing">9. Consider In-app Billing</a></li> <li><a href="#pricing">10. Set prices for your apps</a></li> <li><a href="#localize">11. Start localization early</a></li> -<li><a href="#localize">12. Prepare promotional graphics</a></li> +<li><a href="#graphics">12. Prepare promotional graphics</a></li> <li><a href="#apk">13. Build the release-ready APK</a></li> <li><a href="#product-page">14. Complete the product details</a></li> <li><a href="#badges">15. Use Google Play badges</a></li> diff --git a/docs/html/guide/google/play/expansion-files.jd b/docs/html/guide/google/play/expansion-files.jd index 750e958..f5cda06 100644 --- a/docs/html/guide/google/play/expansion-files.jd +++ b/docs/html/guide/google/play/expansion-files.jd @@ -421,7 +421,7 @@ policy, which captures the expansion file names, sizes, and URLs from the licens <p>To use APK expansion files with your application and provide the best user experience with minimal effort on your behalf, we recommend you use the Downloader Library that's included in the -Google Market Apk Expansion package. This library downloads your expansion files in a +Google Play APK Expansion Library package. This library downloads your expansion files in a background service, shows a user notification with the download status, handles network connectivity loss, resumes the download when possible, and more.</p> @@ -450,8 +450,8 @@ application.</p> <p>First, open the <a href="{@docRoot}sdk/exploring.html">Android SDK Manager</a>, expand <em>Extras</em> and download:</p> <ul> - <li><em>Google Market Licensing package</em></li> - <li><em>Google Market Apk Expansion package</em></li> + <li><em>Google Play Licensing Library package</em></li> + <li><em>Google Play APK Expansion Library package</em></li> </ul> <p>If you're using Eclipse, create a project for each library and add it to your app:</p> diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd index 2de6260..8026b7d 100644 --- a/docs/html/guide/topics/ui/notifiers/notifications.jd +++ b/docs/html/guide/topics/ui/notifiers/notifications.jd @@ -18,6 +18,7 @@ page.title=Notifications <li><a href="#Actions">Notification actions</a></li> <li><a href="#SimpleNotification">Creating a simple notification</a></li> <li><a href="#ApplyStyle">Applying a big view style to a notification</a></li> + <li><a href="#Compatibility">Handling compatibility</a></li> </ol> </li> <li><a href="#Managing">Managing Notifications</a> @@ -91,18 +92,36 @@ page.title=Notifications </p> </div> <p class="note"> - <strong>Note:</strong> This guide refers to the + <strong>Note:</strong> Except where noted, this guide refers to the {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} class in the version 4 <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>. - The class {@link android.app.Notification.Builder Notification.Builder} was added in API - level 11. + The class {@link android.app.Notification.Builder Notification.Builder} was added in Android + 3.0. </p> <!-- ------------------------------------------------------------------------------------------ --> <!-- ------------------------------------------------------------------------------------------ --> <h2 id="NotificationUI">Notification Display Elements</h2> <p> - Notifications in the notification drawer appear in two main visual styles, normal view and - big view. + Notifications in the notification drawer can appear in one of two visual styles, depending on + the version and the state of the drawer: +</p> +<dl> + <dt> + Normal view + </dt> + <dd> + The standard view of the notifications in the notification drawer. + </dd> + <dt> + Big view + </dt> + <dd> + A large view that's visible when the notification is expanded. Big view is part of the + expanded notification feature available as of Android 4.1. + </dd> +</dl> +<p> + These styles are described in the following sections. </p> <!-- ------------------------------------------------------------------------------------------ --> <h3 id="NormalNotify">Normal view</h3> @@ -139,7 +158,7 @@ page.title=Notifications <p> A notification's big view appears only when the notification is expanded, which happens when the notification is at the top of the notification drawer, or when the user expands the - notification with a gesture. + notification with a gesture. Expanded notifications are available starting with Android 4.1. </p> <p> The following screenshot shows an inbox-style notification: @@ -246,10 +265,12 @@ page.title=Notifications </p> <p> A notification can provide multiple actions. You should always define the action that's - triggered when the user touches the notification; usually this action opens an + triggered when the user clicks the notification; usually this action opens an {@link android.app.Activity} in your application. You can also add buttons to the notification that perform additional actions such as snoozing an alarm or responding immediately to a text - message. + message; this feature is available as of Android 4.1. If you use additional action buttons, you + must also make their functionality available in an {@link android.app.Activity} in your app; see + the section <a href="#Compatibility">Handling compatibility</a> for more details. </p> <p> Inside a {@link android.app.Notification}, the action itself is defined by a @@ -257,22 +278,22 @@ page.title=Notifications an {@link android.app.Activity} in your application. To associate the {@link android.app.PendingIntent} with a gesture, call the appropriate method of {@link android.support.v4.app.NotificationCompat.Builder}. For example, if you want to start - {@link android.app.Activity} when the user touches the notification text in + {@link android.app.Activity} when the user clicks the notification text in the notification drawer, you add the {@link android.app.PendingIntent} by calling {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}. </p> <p> - Starting an {@link android.app.Activity} when the user touches the notification is the most + Starting an {@link android.app.Activity} when the user clicks the notification is the most common action scenario. You can also start an {@link android.app.Activity} when the user - dismisses an {@link android.app.Activity}, and you can start an {@link android.app.Activity} - from an action button. To learn more, read the reference guide for + dismisses an {@link android.app.Activity}. In Android 4.1 and later, you can start an + {@link android.app.Activity} from an action button. To learn more, read the reference guide for {@link android.support.v4.app.NotificationCompat.Builder}. </p> <!-- ------------------------------------------------------------------------------------------ --> <h3 id="SimpleNotification">Creating a simple notification</h3> <p> The following snippet illustrates a simple notification that specifies an activity to open when - the user touches the notification. Notice that the code creates a + the user clicks the notification. Notice that the code creates a {@link android.support.v4.app.TaskStackBuilder} object and uses it to create the {@link android.app.PendingIntent} for the action. This pattern is explained in more detail in the section <a href="#NotificationResponse"> @@ -317,6 +338,11 @@ mNotificationManager.notify(mId, mBuilder.build()); Builder.setStyle()} with a big view style object as its argument. </p> <p> + Remember that expanded notifications are not available on platforms prior to Android 4.1. To + learn how to handle notifications for Android 4.1 and for earlier platforms, read the + section <a href="#Compatibility">Handling compatibility</a>. +</p> +<p> For example, the following code snippet demonstrates how to alter the notification created in the previous snippet to use the Inbox big view style: </p> @@ -341,6 +367,47 @@ mBuilder.setStyle(inBoxStyle); ... // Issue the notification here. </pre> +<h3 id="Compatibility">Handling compatibility</h3> +<p> + Not all notification features are available for a particular version, even though + the methods to set them are in the support library class + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. + For example, action buttons, which depend on expanded notifications, only appear on Android + 4.1 and higher, because expanded notifications themselves are only available on + Android 4.1 and higher. +</p> +<p> + To ensure the best compatibility, create notifications with + {@link android.support.v4.app.NotificationCompat NotificationCompat} and its subclasses, + particularly {@link android.support.v4.app.NotificationCompat.Builder + NotificationCompat.Builder}. In addition, follow this process when you implement a notification: +</p> +<ol> + <li> + Provide all of the notification's functionality to all users, regardless of the version + they're using. To do this, verify that all of the functionality is available from an + {@link android.app.Activity} in your app. You may want to add a new + {@link android.app.Activity} to do this. + <p> + For example, if you want to use + {@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} to + provide a control that stops and starts media playback, first implement this + control in an {@link android.app.Activity} in your app. + </p> + </li> + <li> + Ensure that all users can get to the functionality in the {@link android.app.Activity}, + by having it start when users click the notification. To do this, + create a {@link android.app.PendingIntent} for the {@link android.app.Activity}. Call + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} to add the {@link android.app.PendingIntent} to the notification. + </li> + <li> + Now add the expanded notification features you want to use to the notification. Remember + that any functionality you add also has to be available in the {@link android.app.Activity} + that starts when users click the notification. + </li> +</ol> <!-- ------------------------------------------------------------------------------------------ --> <!-- ------------------------------------------------------------------------------------------ --> <h2 id="Managing">Managing Notifications</h2> @@ -355,6 +422,10 @@ mBuilder.setStyle(inBoxStyle); "stacking" the notification; it's described in more detail in the <a href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design guide. </p> +<p class="note"> + <strong>Note:</strong> This Gmail feature requires the "inbox" big view style, which is + part of the expanded notification feature available starting in Android 4.1. +</p> <p> The following section describes how to update notifications and also how to remove them. </p> @@ -417,7 +488,7 @@ numMessages = 0; the notification can be cleared). </li> <li> - The user touches the notification, and you called + The user clicks the notification, and you called {@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()} when you created the notification. </li> @@ -452,7 +523,7 @@ numMessages = 0; start a fresh task, and provide the {@link android.app.PendingIntent} with a back stack that reproduces the application's normal <i>Back</i> behavior. <p> - Notifications from the Gmail app demonstrate this. When you touch a notification for + Notifications from the Gmail app demonstrate this. When you click a notification for a single email message, you see the message itself. Touching <b>Back</b> takes you backwards through Gmail to the Home screen, just as if you had entered Gmail from the Home screen rather than entering it from a notification. @@ -489,7 +560,7 @@ numMessages = 0; Define your application's {@link android.app.Activity} hierarchy in the manifest. <ol style="list-style-type: lower-alpha;"> <li> - Add support for API versions 15 and earlier. To do this, specify the parent of the + Add support for Android 4.0.3 and earlier. To do this, specify the parent of the {@link android.app.Activity} you're starting by adding a <code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> element as the child of the @@ -507,7 +578,7 @@ numMessages = 0; </p> </li> <li> - Also add support for API versions 16 and later. To do this, add the + Also add support for Android 4.1 and later. To do this, add the <code><a href="{@docRoot}guide/topics/manifest/activity-element.html#parent">android:parentActivityName</a></code> attribute to the <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> @@ -738,9 +809,14 @@ mNotificationManager.notify(id, builder.build()); {@link android.widget.ProgressBar} class. </p> <p> - To use a progress indicator, call - {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. The - determinate and indeterminate forms are described in the following sections. + To use a progress indicator on platforms starting with Android 4.0, call + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. For + previous versions, you must create your own custom notification layout that + includes a {@link android.widget.ProgressBar} view. +</p> +<p> + The following sections describe how to display progress in a notification using + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. </p> <!-- ------------------------------------------------------------------------------------------ --> <h3 id="FixedProgress">Displaying a fixed-duration progress indicator</h3> @@ -872,6 +948,10 @@ mNotifyManager.notify(0, mBuilder.build()); {@link android.widget.RemoteViews} defined in a XML layout file. </p> <p> + The height available for a custom notification layout depends on the notification view. Normal + view layouts are limited to 64 dp, and expanded view layouts are limited to 256 dp. +</p> +<p> To define a custom notification layout, start by instantiating a {@link android.widget.RemoteViews} object that inflates an XML layout file. Then, instead of calling methods such as @@ -911,8 +991,8 @@ mNotifyManager.notify(0, mBuilder.build()); <h4>Using style resources for custom notification text</h4> <p> Always use style resources for the text of a custom notification. The background color of the - notification can vary across different devices and platform versions, and using style resources - helps you account for this. Starting in API level 9, the system defined a style for the - standard notification layout text. If you use the same style in applications that target API - level 9 or higher, you'll ensure that your text is visible against the display background. + notification can vary across different devices and versions, and using style resources + helps you account for this. Starting in Android 2.3, the system defined a style for the + standard notification layout text. If you use the same style in applications that target Android + 2.3 or higher, you'll ensure that your text is visible against the display background. </p> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 8374b10..0623a9e 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -1191,6 +1191,11 @@ public class GradientDrawable extends Drawable { return; } + if (mRadius > 0 || mRadiusArray != null) { + mOpaque = false; + return; + } + if (mStrokeWidth > 0 && !isOpaque(mStrokeColor)) { mOpaque = false; return; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e2d7156..6787705 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2433,7 +2433,7 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto } // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) - if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || p->getStrokeCap() != SkPaint::kButt_Cap) { + if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) { mCaches.activeTexture(0); const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top, startAngle, sweepAngle, useCenter, p); diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java index b3e4a88..791d3d0 100644 --- a/location/java/android/location/Geofence.java +++ b/location/java/android/location/Geofence.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * Represents a geographical boundary, also known as a geofence. * - * <p>Currently only circular geofences are supported. + * <p>Currently only circular geofences are supported and they do not support altitude changes. */ public final class Geofence implements Parcelable { /** @hide */ @@ -34,7 +34,7 @@ public final class Geofence implements Parcelable { private final float mRadius; /** - * Create a horizontal, circular geofence. + * Create a circular geofence (on a flat, horizontal plane). * * @param latitude latitude in degrees * @param longitude longitude in degrees diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index f057ebc..4025a7b 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -60,11 +60,19 @@ public class Location implements Parcelable { public static final int FORMAT_SECONDS = 2; /** + * Bundle key for a version of the location that has been fed through + * LocationFudger. Allows location providers to flag locations as being + * safe for use with ACCESS_COARSE_LOCATION permission. + * * @hide */ public static final String EXTRA_COARSE_LOCATION = "coarseLocation"; /** + * Bundle key for a version of the location containing no GPS data. + * Allows location providers to flag locations as being safe to + * feed to LocationFudger. + * * @hide */ public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 59a5624..653e8d1 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -811,6 +811,10 @@ public class LocationManager { /** * Register for fused location updates using a LocationRequest and callback. * + * <p>Upon a location update, the system delivers the new {@link Location} to the + * provided {@link LocationListener}, by calling its {@link + * LocationListener#onLocationChanged} method.</p> + * * <p>The system will automatically select and enable the best providers * to compute a location for your application. It may use only passive * locations, or just a single location source, or it may fuse together @@ -869,6 +873,10 @@ public class LocationManager { /** * Register for fused location updates using a LocationRequest and a pending intent. * + * <p>Upon a location update, the system delivers the new {@link Location} with your provided + * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED} + * in the intent's extras.</p> + * * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}. * * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index b0e5d2c..8a5a739 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -23,6 +23,8 @@ import java.io.PrintWriter; import android.content.Context; import android.location.ILocationManager; import android.location.Location; +import android.location.LocationManager; +import android.location.LocationRequest; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -58,6 +60,21 @@ public abstract class LocationProviderBase { private final ProviderProperties mProperties; private final IBinder mBinder; + /** + * Bundle key for a version of the location containing no GPS data. + * Allows location providers to flag locations as being safe to + * feed to LocationFudger. + */ + public static final String EXTRA_NO_GPS_LOCATION = Location.EXTRA_NO_GPS_LOCATION; + + /** + * Name of the Fused location provider. + * + * <p>This provider combines inputs for all possible location sources + * to provide the best possible Location fix. + */ + public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER; + private final class Service extends ILocationProvider.Stub { @Override public void enable() { diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk index 318782f..a81b9f1 100644 --- a/packages/FusedLocation/Android.mk +++ b/packages/FusedLocation/Android.mk @@ -23,5 +23,6 @@ LOCAL_JAVA_LIBRARIES := com.android.location.provider LOCAL_PACKAGE_NAME := FusedLocation LOCAL_CERTIFICATE := platform +LOCAL_SDK_VERSION := current include $(BUILD_PACKAGE) diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java index 87d56fd..60de79c 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java @@ -20,6 +20,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; +import com.android.location.provider.LocationProviderBase; import com.android.location.provider.ProviderRequestUnbundled; import android.content.Context; @@ -29,6 +30,7 @@ import android.location.LocationManager; import android.location.LocationRequest; import android.os.Bundle; import android.os.Looper; +import android.os.Parcelable; import android.os.SystemClock; import android.os.WorkSource; import android.util.Log; @@ -41,6 +43,7 @@ public class FusionEngine implements LocationListener { private static final String TAG = "FusedLocation"; private static final String NETWORK = LocationManager.NETWORK_PROVIDER; private static final String GPS = LocationManager.GPS_PROVIDER; + private static final String FUSED = LocationProviderBase.FUSED_PROVIDER; public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000; // 11 seconds @@ -72,6 +75,7 @@ public class FusionEngine implements LocationListener { mStats.get(GPS).available = mLocationManager.isProviderEnabled(GPS); mStats.put(NETWORK, new ProviderStats()); mStats.get(NETWORK).available = mLocationManager.isProviderEnabled(NETWORK); + } public void init(Callback callback) { @@ -226,10 +230,24 @@ public class FusionEngine implements LocationListener { } else { mFusedLocation = new Location(mNetworkLocation); } + mFusedLocation.setProvider(FUSED); if (mNetworkLocation != null) { - mFusedLocation.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, mNetworkLocation); + // copy NO_GPS_LOCATION extra from mNetworkLocation into mFusedLocation + Bundle srcExtras = mNetworkLocation.getExtras(); + if (srcExtras != null) { + Parcelable srcParcelable = + srcExtras.getParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION); + if (srcParcelable instanceof Location) { + Bundle dstExtras = mFusedLocation.getExtras(); + if (dstExtras == null) { + dstExtras = new Bundle(); + mFusedLocation.setExtras(dstExtras); + } + dstExtras.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, + (Location) srcParcelable); + } + } } - mFusedLocation.setProvider(LocationManager.FUSED_PROVIDER); mCallback.reportLocation(mFusedLocation); } diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml index e940c85..8fdde92 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml @@ -41,8 +41,7 @@ android:fadingEdge="horizontal" android:scrollbars="none" android:layout_gravity="right" - android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length" - android:importantForAccessibility="no"> + android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length"> <LinearLayout android:id="@+id/recents_linear_layout" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml index 12599f8..7335f86 100644 --- a/packages/SystemUI/res/layout/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml @@ -45,8 +45,7 @@ android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length" android:layout_gravity="bottom|left" android:clipToPadding="false" - android:clipChildren="false" - android:importantForAccessibility="no"> + android:clipChildren="false"> <LinearLayout android:id="@+id/recents_linear_layout" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/system_bar_recent_panel.xml b/packages/SystemUI/res/layout/system_bar_recent_panel.xml index 8afed22..3951bba 100644 --- a/packages/SystemUI/res/layout/system_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout/system_bar_recent_panel.xml @@ -49,8 +49,7 @@ android:fadingEdgeLength="20dip" android:layout_gravity="bottom|left" android:clipToPadding="false" - android:clipChildren="false" - android:importantForAccessibility="no"> + android:clipChildren="false"> <LinearLayout android:id="@+id/recents_linear_layout" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index c34ca3e..7f7a51c 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="7219575858348719790">"Dagdrømme"</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">"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-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index d5af0cd..d619de1 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -176,7 +176,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="7219575858348719790">"Activar Daydreams"</string> + <string name="start_dreams" msgid="7219575858348719790">"Activar protector"</string> <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string> <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avión"</string> <string name="quick_settings_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 e8bc08d..19de9b6 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="7219575858348719790">"Suspender"</string> + <string name="start_dreams" msgid="7219575858348719790">"Salvapantallas"</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-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index a12ccf0..babbcce 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -176,7 +176,7 @@ <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="7219575858348719790">"Écran de veille"</string> + <string name="start_dreams" msgid="7219575858348719790">"Écran de veille interactif"</string> <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-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index a05aa8f..c454bb1 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="7219575858348719790">"Śnij na jawie"</string> + <string name="start_dreams" msgid="7219575858348719790">"Wygaszacz ekranu"</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 1ca085c..1b8370b 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -174,7 +174,7 @@ <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="7219575858348719790">"Sonho"</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 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> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index fba3da5..7e2d030 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="7219575858348719790">"Ndoto ya mchana"</string> + <string name="start_dreams" msgid="7219575858348719790">"Hali Tulivu"</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/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index 599b7e2..cc9c601 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -202,21 +202,18 @@ class QuickSettings { Log.e(TAG, "Couldn't get user info", e); } final int userId = userInfo.id; + final String userName = userInfo.name; final Context context = currentUserContext; mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { @Override protected Pair<String, Drawable> doInBackground(Void... params) { - final Cursor cursor = context.getContentResolver().query( - Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME}, - null, null, null); final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); // Fall back to the UserManager nickname if we can't read the name from the local // profile below. - String nickName = um.getUserName(); - String name = nickName; + String name = userName; Drawable avatar = null; Bitmap rawAvatar = um.getUserIcon(userId); if (rawAvatar != null) { @@ -225,17 +222,23 @@ class QuickSettings { avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); } - // Try and read the display name from the local profile - if (cursor != null) { - try { - if (cursor.moveToFirst()) { - name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); + // If it's a single-user device, get the profile name, since the nickname is not + // usually valid + if (um.getUsers().size() <= 1) { + // Try and read the display name from the local profile + final Cursor cursor = context.getContentResolver().query( + Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME}, + null, null, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); + } + } finally { + cursor.close(); } - } finally { - cursor.close(); } } - return new Pair<String, Drawable>(name, avatar); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index fefd4fb..de028a4 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -412,7 +412,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mForceStatusBar; boolean mForceStatusBarFromKeyguard; boolean mHideLockScreen; - boolean mDismissKeyguard; + + // States of keyguard dismiss. + private static final int DISMISS_KEYGUARD_NONE = 0; // Keyguard not being dismissed. + private static final int DISMISS_KEYGUARD_START = 1; // Keyguard needs to be dismissed. + private static final int DISMISS_KEYGUARD_CONTINUE = 2; // Keyguard has been dismissed. + int mDismissKeyguard = DISMISS_KEYGUARD_NONE; + + /** The window that is currently dismissing the keyguard. Dismissing the keyguard must only + * be done once per window. */ + private WindowState mWinDismissingKeyguard; + boolean mShowingLockscreen; boolean mShowingDream; boolean mDreamingLockscreen; @@ -2921,6 +2931,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { mTopFullscreenOpaqueWindowState = null; mForceStatusBar = false; @@ -2928,12 +2939,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHideLockScreen = false; mAllowLockscreenWhenOn = false; - mDismissKeyguard = false; + mDismissKeyguard = DISMISS_KEYGUARD_NONE; mShowingLockscreen = false; mShowingDream = false; } /** {@inheritDoc} */ + @Override public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" @@ -2953,10 +2965,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean applyWindow = attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW; if (attrs.type == TYPE_DREAM) { - mShowingDream = true; - if (!mDreamingLockscreen) { - applyWindow = true; - } else if (win.isVisibleLw() && win.hasDrawnLw()) { + // If the lockscreen was showing when the dream started then wait + // for the dream to draw before hiding the lockscreen. + if (!mDreamingLockscreen + || (win.isVisibleLw() && win.hasDrawnLw())) { + mShowingDream = true; applyWindow = true; } } @@ -2971,9 +2984,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHideLockScreen = true; mForceStatusBarFromKeyguard = false; } - if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0) { + if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0 + && mDismissKeyguard == DISMISS_KEYGUARD_NONE) { if (DEBUG_LAYOUT) Log.v(TAG, "Setting mDismissKeyguard to true by win " + win); - mDismissKeyguard = true; + mDismissKeyguard = mWinDismissingKeyguard == win ? + DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; + mWinDismissingKeyguard = win; mForceStatusBarFromKeyguard = false; } if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { @@ -2984,6 +3000,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ + @Override public int finishPostLayoutPolicyLw() { int changes = 0; boolean topIsFullscreen = false; @@ -2992,8 +3009,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { ? mTopFullscreenOpaqueWindowState.getAttrs() : null; - // If we are not currently showing a dream, then update the lockscreen - // state that will apply if a dream is shown next time. + // If we are not currently showing a dream then remember the current + // lockscreen state. We will use this to determine whether the dream + // started while the lockscreen was showing and remember this state + // while the dream is showing. if (!mShowingDream) { mDreamingLockscreen = mShowingLockscreen; } @@ -3023,7 +3042,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mStatusBar.hideLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; - mHandler.post(new Runnable() { public void run() { + mHandler.post(new Runnable() { + @Override + public void run() { try { IStatusBarService statusbar = getStatusBarService(); if (statusbar != null) { @@ -3051,7 +3072,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mKeyguard != null) { if (localLOGV) Log.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard=" + mHideLockScreen); - if (mDismissKeyguard && !mKeyguardMediator.isSecure()) { + if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardMediator.isSecure()) { if (mKeyguard.hideLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG @@ -3059,6 +3080,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mKeyguardMediator.isShowing()) { mHandler.post(new Runnable() { + @Override public void run() { mKeyguardMediator.keyguardDone(false, false); } @@ -3071,7 +3093,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { | FINISH_LAYOUT_REDO_WALLPAPER; } mKeyguardMediator.setHidden(true); + } else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) { + // This is the case of keyguard isSecure() and not mHideLockScreen. + if (mDismissKeyguard == DISMISS_KEYGUARD_START) { + // Only launch the next keyguard unlock window once per window. + if (mKeyguard.showLw(true)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT + | FINISH_LAYOUT_REDO_CONFIG + | FINISH_LAYOUT_REDO_WALLPAPER; + } + mKeyguardMediator.setHidden(false); + mHandler.post(new Runnable() { + @Override + public void run() { + mKeyguardMediator.dismiss(); + } + }); + } } else { + mWinDismissingKeyguard = null; if (mKeyguard.showLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG @@ -4549,6 +4589,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard); pw.print(prefix); pw.print("mDismissKeyguard="); pw.print(mDismissKeyguard); + pw.print(" mWinDismissingKeyguard="); pw.print(mWinDismissingKeyguard); pw.print(" mHomePressed="); pw.println(mHomePressed); pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn); pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java index b38a9ed..3dd0a8f 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java @@ -21,12 +21,15 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Point; import android.os.Handler; import android.os.SystemClock; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; @@ -47,10 +50,11 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli private final Handler mHandler = new Handler(); private final KeyguardActivityLauncher mActivityLauncher; private final Callbacks mCallbacks; + private final WindowManager mWindowManager; + private final Point mRenderedSize = new Point(); private View mWidgetView; private long mLaunchCameraStart; - private boolean mRendered; private boolean mActive; private boolean mChallengeActive; private boolean mTransitioning; @@ -81,6 +85,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli mCallbacks = callbacks; mActivityLauncher = activityLauncher; + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } public static CameraWidgetFrame create(Context context, Callbacks callbacks, @@ -141,16 +146,22 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli } 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 (mRenderedSize.x == width && mRenderedSize.y == height) { + if (DEBUG) Log.d(TAG, String.format("already rendered at size=%sx%s", + width, height)); + return; + } if (width == 0 || height == 0) { return; } + if (DEBUG) Log.d(TAG, String.format("render size=%sx%s instance=%s at %s", + width, height, + Integer.toHexString(hashCode()), + SystemClock.uptimeMillis())); + Bitmap offscreen = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(offscreen); mWidgetView.measure( @@ -159,7 +170,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli mWidgetView.layout(0, 0, width, height); mWidgetView.draw(c); ((ImageView)getChildAt(0)).setImageBitmap(offscreen); - mRendered = true; + mRenderedSize.set(width, height); } catch (Throwable t) { Log.w(TAG, "Error rendering camera widget", t); removeAllViews(); @@ -200,6 +211,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli scaleX, scaleY, startCenter, finishCenter)); + enableWindowExitAnimation(false); animate() .scaleX(scale) .scaleY(scale) @@ -305,11 +317,27 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli setScaleX(1); setScaleY(1); setTranslationY(0); + enableWindowExitAnimation(true); } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s", + w, h, oldw, oldh, SystemClock.uptimeMillis())); mHandler.post(mRenderRunnable); + super.onSizeChanged(w, h, oldw, oldh); + } + + private void enableWindowExitAnimation(boolean isEnabled) { + View root = getRootView(); + ViewGroup.LayoutParams lp = root.getLayoutParams(); + if (!(lp instanceof WindowManager.LayoutParams)) + return; + WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp; + int newWindowAnimations = isEnabled ? com.android.internal.R.style.Animation_LockScreen : 0; + if (newWindowAnimations != wlp.windowAnimations) { + wlp.windowAnimations = newWindowAnimations; + mWindowManager.updateViewLayout(root, wlp); + } } } 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 cae598c..faf0ca0 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java @@ -151,13 +151,6 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } boolean mWasRunning = mIsRunning; - try { - if (mService != null) { - mService.makeInvisible(); - } - } catch (RemoteException e) { - Log.e(TAG, "Caught exception making Face Unlock invisible: " + e.toString()); - } stopUi(); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java index 9c21830..db36bcc 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java @@ -87,16 +87,20 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } } + protected abstract int getPasswordTextViewId(); protected abstract void resetState(); @Override protected void onFinishInflate() { mLockPatternUtils = new LockPatternUtils(mContext); - mPasswordEntry = (TextView) findViewById(R.id.passwordEntry); + mPasswordEntry = (TextView) findViewById(getPasswordTextViewId()); mPasswordEntry.setOnEditorActionListener(this); mPasswordEntry.addTextChangedListener(this); + // Set selected property on so the view can send accessibility events. + mPasswordEntry.setSelected(true); + // Poke the wakelock any time the text is selected or modified mPasswordEntry.setOnClickListener(new OnClickListener() { public void onClick(View v) { 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 3a01e64..3b2ded2 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -25,19 +25,22 @@ import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; +import android.os.Bundle; 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; @@ -48,13 +51,13 @@ import android.view.View; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.widget.RemoteViews.OnClickHandler; -import android.widget.TextView; import com.android.internal.R; import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.internal.widget.LockPatternUtils; import java.io.File; +import java.util.ArrayList; import java.util.List; public class KeyguardHostView extends KeyguardViewBase { @@ -66,11 +69,6 @@ public class KeyguardHostView extends KeyguardViewBase { // also referenced in SecuritySettings.java static final int APPWIDGET_HOST_ID = 0x4B455947; - // transport control states - private static final int TRANSPORT_GONE = 0; - private static final int TRANSPORT_INVISIBLE = 1; - private static final int TRANSPORT_VISIBLE = 2; - private AppWidgetHost mAppWidgetHost; private KeyguardWidgetPager mAppWidgetContainer; private KeyguardSecurityViewFlipper mSecurityViewContainer; @@ -90,7 +88,6 @@ public class KeyguardHostView extends KeyguardViewBase { private KeyguardViewStateManager mViewStateManager; private Rect mTempRect = new Rect(); - private int mTransportState = TRANSPORT_GONE; /*package*/ interface TransportCallback { void onListenerDetached(); @@ -144,7 +141,7 @@ public class KeyguardHostView extends KeyguardViewBase { private int getWidgetPosition(int id) { final int children = mAppWidgetContainer.getChildCount(); for (int i = 0; i < children; i++) { - if (mAppWidgetContainer.getChildAt(i).getId() == id) { + if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) { return i; } } @@ -153,6 +150,8 @@ public class KeyguardHostView extends KeyguardViewBase { @Override protected void onFinishInflate() { + mViewStateManager = new KeyguardViewStateManager(); + // 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); @@ -162,8 +161,8 @@ public class KeyguardHostView extends KeyguardViewBase { addDefaultWidgets(); addWidgetsFromSettings(); + mSwitchPageRunnable.run(); - mViewStateManager = new KeyguardViewStateManager(); SlidingChallengeLayout slider = (SlidingChallengeLayout) findViewById(R.id.sliding_layout); if (slider != null) { @@ -217,7 +216,6 @@ public class KeyguardHostView extends KeyguardViewBase { protected void onAttachedToWindow() { super.onAttachedToWindow(); mAppWidgetHost.startListening(); - post(mSwitchPageRunnable); } @Override @@ -676,13 +674,10 @@ public class KeyguardHostView extends KeyguardViewBase { // Find and show this child. final int childCount = mSecurityViewContainer.getChildCount(); - // Do flip animation to the next screen - if (false) { - mSecurityViewContainer.setInAnimation( - AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in)); - mSecurityViewContainer.setOutAnimation( - AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out)); - } + mSecurityViewContainer.setInAnimation( + AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in)); + mSecurityViewContainer.setOutAnimation( + AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out)); final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); for (int i = 0; i < childCount; i++) { if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { @@ -864,7 +859,8 @@ public class KeyguardHostView extends KeyguardViewBase { @Override LockPatternUtils getLockPatternUtils() { return mLockPatternUtils; - }}; + } + }; private void addDefaultWidgets() { LayoutInflater inflater = LayoutInflater.from(mContext); @@ -888,14 +884,22 @@ public class KeyguardHostView extends KeyguardViewBase { @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)); + int defaultIconId = 0; + Resources res = KeyguardHostView.this.getContext().getResources(); + ComponentName clock = new ComponentName( + res.getString(R.string.widget_default_package_name), + res.getString(R.string.widget_default_class_name)); + try { + ActivityInfo activityInfo = + mContext.getPackageManager().getActivityInfo(clock, 0); + if (activityInfo != null) { + defaultIconId = activityInfo.icon; + } + } catch (PackageManager.NameNotFoundException e) { + defaultIconId = 0; + } + launchPickActivityIntent(R.string.widget_default, defaultIconId, clock, + LockPatternUtils.EXTRA_DEFAULT_WIDGET); } }); mCallback.dismiss(false); @@ -906,6 +910,85 @@ public class KeyguardHostView extends KeyguardViewBase { initializeTransportControl(); } + private void launchPickActivityIntent(int defaultLabelId, int defaultIconId, + ComponentName defaultComponentName, String defaultTag) { + // Create intent to pick widget + Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK); + + int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); + if (appWidgetId != -1) { + pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false); + pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, + AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); + + // Add an custom entry for the default + AppWidgetProviderInfo defaultInfo = new AppWidgetProviderInfo(); + ArrayList<AppWidgetProviderInfo> extraInfos = new ArrayList<AppWidgetProviderInfo>(); + defaultInfo.label = getResources().getString(defaultLabelId); + defaultInfo.icon = defaultIconId; + defaultInfo.provider = defaultComponentName; + extraInfos.add(defaultInfo); + + ArrayList<Bundle> extraExtras = new ArrayList<Bundle>(); + Bundle b = new Bundle(); + b.putBoolean(defaultTag, true); + extraExtras.add(b); + + // Launch the widget picker + pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, extraInfos); + pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, extraExtras); + pickIntent.putExtra(Intent.EXTRA_INTENT, getBaseIntent()); + pickIntent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + mContext.startActivityAsUser(pickIntent, + new UserHandle(UserHandle.USER_CURRENT)); + } else { + Log.e(TAG, "Unable to allocate an AppWidget id in lock screen"); + } + } + + private Intent getBaseIntent() { + Intent baseIntent = new Intent(Intent.ACTION_MAIN, null); + baseIntent.addCategory(Intent.CATEGORY_DEFAULT); + + Bundle options = new Bundle(); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, + AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); + baseIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); + return baseIntent; + } + + private void removeTransportFromWidgetPager() { + int page = getWidgetPosition(R.id.keyguard_transport_control); + if (page != -1) { + mAppWidgetContainer.removeWidget(mTransportControl); + + // XXX keep view attached so we still get show/hide events from AudioManager + KeyguardHostView.this.addView(mTransportControl); + mTransportControl.setVisibility(View.GONE); + mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_GONE); + mTransportControl.post(mSwitchPageRunnable); + } + } + + private void addTransportToWidgetPager() { + if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { + KeyguardHostView.this.removeView(mTransportControl); + // insert to left of camera if it exists, otherwise after right-most widget + int lastWidget = mAppWidgetContainer.getChildCount() - 1; + int position = 0; // handle no widget case + if (lastWidget >= 0) { + position = isCameraPage(lastWidget) ? lastWidget : lastWidget + 1; + } + mAppWidgetContainer.addWidget(mTransportControl, position); + mTransportControl.setVisibility(View.VISIBLE); + } + } + private void initializeTransportControl() { mTransportControl = (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); @@ -917,24 +1000,14 @@ public class KeyguardHostView extends KeyguardViewBase { mTransportControl.setKeyguardCallback(new TransportCallback() { @Override public void onListenerDetached() { - int page = getWidgetPosition(R.id.keyguard_transport_control); - if (page != -1) { - mAppWidgetContainer.removeView(mTransportControl); - // XXX keep view attached so we still get show/hide events from AudioManager - KeyguardHostView.this.addView(mTransportControl); - mTransportControl.setVisibility(View.GONE); - mTransportState = TRANSPORT_GONE; - mTransportControl.post(mSwitchPageRunnable); - } + removeTransportFromWidgetPager(); + mTransportControl.post(mSwitchPageRunnable); } @Override public void onListenerAttached() { - if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { - KeyguardHostView.this.removeView(mTransportControl); - mAppWidgetContainer.addView(mTransportControl, 0); - mTransportControl.setVisibility(View.VISIBLE); - } + // Transport will be added when playstate changes... + mTransportControl.post(mSwitchPageRunnable); } @Override @@ -1027,7 +1100,7 @@ public class KeyguardHostView extends KeyguardViewBase { saveStickyWidgetIndex(); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); - ss.transportState = mTransportState; + ss.transportState = mViewStateManager.getTransportState(); return ss; } @@ -1040,7 +1113,7 @@ public class KeyguardHostView extends KeyguardViewBase { } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); - mTransportState = ss.transportState; + mViewStateManager.setTransportState(ss.transportState); post(mSwitchPageRunnable); } @@ -1054,12 +1127,14 @@ public class KeyguardHostView extends KeyguardViewBase { } private void showAppropriateWidgetPage() { - boolean isMusicPlaying = - mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE; + int state = mViewStateManager.getTransportState(); + boolean isMusicPlaying = mTransportControl.isMusicPlaying() + || state == KeyguardViewStateManager.TRANSPORT_VISIBLE; if (isMusicPlaying) { - mTransportState = TRANSPORT_VISIBLE; - } else if (mTransportState == TRANSPORT_VISIBLE) { - mTransportState = TRANSPORT_INVISIBLE; + mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_VISIBLE); + addTransportToWidgetPager(); + } else if (state == KeyguardViewStateManager.TRANSPORT_VISIBLE) { + mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_INVISIBLE); } int pageToShow = getAppropriateWidgetPage(isMusicPlaying); mAppWidgetContainer.setCurrentPage(pageToShow); @@ -1081,7 +1156,7 @@ public class KeyguardHostView extends KeyguardViewBase { // if music playing, show transport if (isMusicPlaying) { if (DEBUG) Log.d(TAG, "Music playing, show transport"); - return mAppWidgetContainer.indexOfChild(mTransportControl); + return mAppWidgetContainer.getWidgetPageIndex(mTransportControl); } // if we have a valid sticky widget, show it @@ -1119,6 +1194,10 @@ public class KeyguardHostView extends KeyguardViewBase { } private void enableUserSelectorIfNecessary() { + if (!UserManager.supportsMultipleUsers()) { + return; // device doesn't support multi-user mode + } + // if there are multiple users, we need to enable to multi-user switcher UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); List<UserInfo> users = mUm.getUsers(true); @@ -1201,4 +1280,10 @@ public class KeyguardHostView extends KeyguardViewBase { return false; } + /** + * Dismisses the keyguard by going to the next screen or making it gone. + */ + public void dismiss() { + showNextSecurityScreenOrFinish(false); + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java index ca78cf9..5e331e1 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java @@ -16,6 +16,9 @@ package com.android.internal.policy.impl.keyguard; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.content.ContentResolver; import android.content.Context; import android.os.Handler; @@ -39,11 +42,15 @@ class KeyguardMessageArea extends TextView { static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery; static final int SECURITY_MESSAGE_DURATION = 5000; - static final String SEPARATOR = " "; + protected static final int FADE_DURATION = 750; + static final String SEPARATOR = " "; // are we showing battery information? boolean mShowingBatteryInfo = false; + // is the bouncer up? + boolean mShowingBouncer = false; + // last known plugged in state boolean mPluggedIn = false; @@ -68,7 +75,11 @@ class KeyguardMessageArea extends TextView { public void run() { mMessage = null; mShowingMessage = false; - update(); + if (mShowingBouncer) { + hideMessage(FADE_DURATION, true); + } else { + update(); + } } }; @@ -103,6 +114,18 @@ class KeyguardMessageArea extends TextView { } @Override + public void showBouncer(int duration) { + mMessageArea.hideMessage(duration, false); + mMessageArea.mShowingBouncer = true; + } + + @Override + public void hideBouncer(int duration) { + mMessageArea.showMessage(duration); + mMessageArea.mShowingBouncer = false; + } + + @Override public void setTimeout(int timeoutMs) { mMessageArea.mTimeout = timeoutMs; } @@ -139,6 +162,7 @@ class KeyguardMessageArea extends TextView { } public void securityMessageChanged() { + setAlpha(1f); mShowingMessage = true; update(); mHandler.removeCallbacks(mClearMessageRunnable); @@ -212,4 +236,23 @@ class KeyguardMessageArea extends TextView { return string; } + private void hideMessage(int duration, boolean thenUpdate) { + Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f); + anim.setDuration(duration); + if (thenUpdate) { + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + update(); + } + }); + } + anim.start(); + } + + private void showMessage(int duration) { + Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f); + anim.setDuration(duration); + anim.start(); + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java index bea9aec..1255712 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java @@ -47,6 +47,11 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView } @Override + protected int getPasswordTextViewId() { + return R.id.pinEntry; + } + + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -59,6 +64,7 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView verifyPasswordAndUnlock(); } }); + ok.setOnHoverListener(new NumPadKey.LiftToActivateListener(getContext())); } // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, 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 b6334f0..b35450c 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java @@ -60,6 +60,11 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override + protected int getPasswordTextViewId() { + return R.id.passwordEntry; + } + + @Override public boolean needsInput() { return true; } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java index f6a90c5..04ab0a2 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java @@ -1,11 +1,20 @@ package com.android.internal.policy.impl.keyguard; +import android.animation.Animator; +import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.FrameLayout; +import com.android.internal.R; + public class KeyguardSecurityContainer extends FrameLayout { + private float mBackgroundAlpha; + private Drawable mBackgroundDrawable; + public KeyguardSecurityContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -16,5 +25,44 @@ public class KeyguardSecurityContainer extends FrameLayout { public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mBackgroundDrawable = context.getResources().getDrawable(R.drawable.kg_bouncer_bg_white); + } + + public void setBackgroundAlpha(float alpha) { + if (Float.compare(mBackgroundAlpha, alpha) != 0) { + mBackgroundAlpha = alpha; + invalidate(); + } + } + + public float getBackgroundAlpha() { + return mBackgroundAlpha; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (mBackgroundAlpha > 0.0f && mBackgroundDrawable != null) { + Drawable bg = mBackgroundDrawable; + bg.setAlpha((int) (mBackgroundAlpha * 255)); + bg.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + bg.draw(canvas); + } + super.dispatchDraw(canvas); + } + + public void showBouncer(int duration) { + SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this); + message.showBouncer(duration); + Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 1f); + anim.setDuration(duration); + anim.start(); + } + + public void hideBouncer(int duration) { + SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this); + message.hideBouncer(duration); + Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 0f); + anim.setDuration(duration); + anim.start(); } } 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 b4bd6e9..7100f1c 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java @@ -327,6 +327,14 @@ class KeyguardStatusViewManager implements SecurityMessageDisplay { } @Override + public void showBouncer(int duration) { + } + + @Override + public void hideBouncer(int duration) { + } + + @Override public void setTimeout(int timeout_ms) { } 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 89f220a..d284602 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java @@ -40,6 +40,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -49,14 +50,13 @@ import java.lang.ref.WeakReference; /** * This is the widget responsible for showing music controls in keyguard. */ -public class KeyguardTransportControlView extends KeyguardWidgetFrame implements OnClickListener { +public class KeyguardTransportControlView extends FrameLayout implements OnClickListener { private static final int MSG_UPDATE_STATE = 100; private static final int MSG_SET_METADATA = 101; private static final int MSG_SET_TRANSPORT_CONTROLS = 102; private static final int MSG_SET_ARTWORK = 103; private static final int MSG_SET_GENERATION_ID = 104; - private static final int MAXDIM = 512; private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s protected static final boolean DEBUG = false; protected static final String TAG = "TransportControlView"; @@ -260,14 +260,6 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements mAttached = false; } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); -// int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight())); -// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim); -// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim); - } - class Metadata { private String artist; private String trackTitle; 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 9fa14f5..b224c08 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -65,6 +65,7 @@ public class KeyguardViewManager { private FrameLayout mKeyguardHost; private KeyguardHostView mKeyguardView; + private boolean mScreenOn = false; private LockPatternUtils mLockPatternUtils; public interface ShowListener { @@ -302,6 +303,7 @@ public class KeyguardViewManager { public synchronized void onScreenTurnedOff() { if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); + mScreenOn = false; if (mKeyguardView != null) { mKeyguardView.onScreenTurnedOff(); } @@ -310,6 +312,7 @@ public class KeyguardViewManager { public synchronized void onScreenTurnedOn( final KeyguardViewManager.ShowListener showListener) { if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); + mScreenOn = true; if (mKeyguardView != null) { mKeyguardView.onScreenTurnedOn(); @@ -398,6 +401,15 @@ public class KeyguardViewManager { } /** + * Dismisses the keyguard by going to the next screen or making it gone. + */ + public synchronized void dismiss() { + if (mScreenOn) { + mKeyguardView.dismiss(); + } + } + + /** * @return Whether the keyguard is showing */ public synchronized boolean isShowing() { 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 bc12e96..53cbb39 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -813,9 +813,7 @@ public class KeyguardViewMediator { } /** - * Enable the keyguard if the settings are appropriate. Return true if all - * work that will happen is done; returns false if the caller can wait for - * the keyguard to be shown. + * Enable the keyguard if the settings are appropriate. */ private void doKeyguardLocked(Bundle options) { // if another app is disabling us, don't show @@ -867,6 +865,13 @@ public class KeyguardViewMediator { } /** + * Dismiss the keyguard through the security layers. + */ + public void dismiss() { + mKeyguardViewManager.dismiss(); + } + + /** * Send message to keyguard telling it to reset its state. * @param options options about how to show the keyguard * @see #handleReset() diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java index 85245ba..c89e880 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java @@ -32,6 +32,13 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle private static final int SCREEN_ON_RING_HINT_DELAY = 300; Handler mMainQueue = new Handler(Looper.myLooper()); + // transport control states + static final int TRANSPORT_GONE = 0; + static final int TRANSPORT_INVISIBLE = 1; + static final int TRANSPORT_VISIBLE = 2; + + private int mTransportState = TRANSPORT_GONE; + int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE; // Paged view state @@ -58,6 +65,13 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle return false; } + public boolean isChallengeOverlapping() { + if (mChallengeLayout != null) { + return mChallengeLayout.isChallengeOverlapping(); + } + return false; + } + public void setSecurityViewContainer(KeyguardSecurityView container) { mKeyguardSecurityContainer = container; } @@ -79,6 +93,14 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle mChallengeLayout.showBouncer(); } + public void fadeOutSecurity(int duration) { + ((View) mKeyguardSecurityContainer).animate().alpha(0).setDuration(duration); + } + + public void fadeInSecurity(int duration) { + ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration); + } + public void onPageSwitch(View newPage, int newPageIndex) { // Reset the previous page size and ensure the current page is sized appropriately. // We only modify the page state if it is not currently under control by the slider. @@ -207,4 +229,12 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION); } + + public void setTransportState(int state) { + mTransportState = state; + } + + public int getTransportState() { + return mTransportState; + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java index cf16ef2..2e83b42 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java @@ -15,9 +15,19 @@ */ package com.android.internal.policy.impl.keyguard; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; import android.util.AttributeSet; import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import java.util.ArrayList; import com.android.internal.R; @@ -26,6 +36,8 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { private float mAdjacentPagesAngle; private static float MAX_SCROLL_PROGRESS = 1.3f; private static float CAMERA_DISTANCE = 10000; + protected AnimatorSet mChildrenTransformsAnimator; + float[] mTmpTransform = new float[3]; public KeyguardWidgetCarousel(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -51,10 +63,10 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { float scrollProgress = getScrollProgress(screenCenter, child, index); if (!isOverScrollChild(index, scrollProgress)) { scrollProgress = getBoundedScrollProgress(screenCenter, child, index); - float alpha = 1 - Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); + float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); return alpha; } else { - return 1f; + return 1.0f; } } @@ -67,22 +79,22 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { for (int i = 0; i < getChildCount(); i++) { KeyguardWidgetFrame child = getWidgetPageAt(i); if (child != null) { - float alpha = getAlphaForPage(screenCenter, i); - child.setBackgroundAlpha(alpha); - child.setContentAlpha(alpha); + child.setBackgroundAlpha(getOutlineAlphaForPage(screenCenter, i)); + child.setContentAlpha(getAlphaForPage(screenCenter, i)); } } } - } @Override protected void screenScrolled(int screenCenter) { mScreenCenter = screenCenter; updatePageAlphaValues(screenCenter); + if (isReordering(false)) return; for (int i = 0; i < getChildCount(); i++) { KeyguardWidgetFrame v = getWidgetPageAt(i); float scrollProgress = getScrollProgress(screenCenter, v, i); + float boundedProgress = getBoundedScrollProgress(screenCenter, v, i); if (v == mDragView || v == null) continue; v.setCameraDistance(CAMERA_DISTANCE); @@ -90,17 +102,15 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); } else { - scrollProgress = getBoundedScrollProgress(screenCenter, v, i); int width = v.getMeasuredWidth(); - float pivotX = (width / 2f) + scrollProgress * (width / 2f); + float pivotX = (width / 2f) + boundedProgress * (width / 2f); float pivotY = v.getMeasuredHeight() / 2; - float rotationY = - mAdjacentPagesAngle * scrollProgress; + float rotationY = - mAdjacentPagesAngle * boundedProgress; v.setPivotX(pivotX); v.setPivotY(pivotY); v.setRotationY(rotationY); v.setOverScrollAmount(0f, false); } - float alpha = v.getAlpha(); // If the view has 0 alpha, we set it to be invisible so as to prevent // it from accepting touches @@ -111,4 +121,139 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { } } } + + void animatePagesToNeutral() { + if (mChildrenTransformsAnimator != null) { + mChildrenTransformsAnimator.cancel(); + mChildrenTransformsAnimator = null; + } + + int count = getChildCount(); + PropertyValuesHolder alpha; + PropertyValuesHolder outlineAlpha; + PropertyValuesHolder rotationY; + ArrayList<Animator> anims = new ArrayList<Animator>(); + + for (int i = 0; i < count; i++) { + KeyguardWidgetFrame child = getWidgetPageAt(i); + boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); + if (!inVisibleRange) { + child.setRotationY(0f); + } + alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f); + outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", + KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); + rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f); + ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY); + child.setVisibility(VISIBLE); + if (!inVisibleRange) { + a.setInterpolator(mSlowFadeInterpolator); + } + anims.add(a); + } + + int duration = REORDERING_ZOOM_IN_OUT_DURATION; + mChildrenTransformsAnimator = new AnimatorSet(); + mChildrenTransformsAnimator.playTogether(anims); + + mChildrenTransformsAnimator.setDuration(duration); + mChildrenTransformsAnimator.start(); + } + + private void getTransformForPage(int screenCenter, int index, float[] transform) { + View child = getChildAt(index); + float boundedProgress = getBoundedScrollProgress(screenCenter, child, index); + float rotationY = - mAdjacentPagesAngle * boundedProgress; + int width = child.getMeasuredWidth(); + float pivotX = (width / 2f) + boundedProgress * (width / 2f); + float pivotY = child.getMeasuredHeight() / 2; + + transform[0] = pivotX; + transform[1] = pivotY; + transform[2] = rotationY; + } + + Interpolator mFastFadeInterpolator = new Interpolator() { + Interpolator mInternal = new DecelerateInterpolator(1.5f); + float mFactor = 2.5f; + @Override + public float getInterpolation(float input) { + return mInternal.getInterpolation(Math.min(mFactor * input, 1f)); + } + }; + + Interpolator mSlowFadeInterpolator = new Interpolator() { + Interpolator mInternal = new AccelerateInterpolator(1.5f); + float mFactor = 1.3f; + @Override + public float getInterpolation(float input) { + input -= (1 - 1 / mFactor); + input = mFactor * Math.max(input, 0f); + return mInternal.getInterpolation(input); + } + }; + + void animatePagesToCarousel() { + if (mChildrenTransformsAnimator != null) { + mChildrenTransformsAnimator.cancel(); + mChildrenTransformsAnimator = null; + } + + int count = getChildCount(); + PropertyValuesHolder alpha; + PropertyValuesHolder outlineAlpha; + PropertyValuesHolder rotationY; + PropertyValuesHolder pivotX; + PropertyValuesHolder pivotY; + ArrayList<Animator> anims = new ArrayList<Animator>(); + + for (int i = 0; i < count; i++) { + KeyguardWidgetFrame child = getWidgetPageAt(i); + float finalAlpha = getAlphaForPage(mScreenCenter, i); + float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i); + getTransformForPage(mScreenCenter, i, mTmpTransform); + + boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); + + ObjectAnimator a; + alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha); + outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha); + pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]); + pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]); + rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]); + + if (inVisibleRange) { + // for the central pages we animate into a rotated state + a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, + pivotX, pivotY, rotationY); + } else { + a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha); + a.setInterpolator(mFastFadeInterpolator); + } + anims.add(a); + } + + int duration = REORDERING_ZOOM_IN_OUT_DURATION; + mChildrenTransformsAnimator = new AnimatorSet(); + mChildrenTransformsAnimator.playTogether(anims); + + mChildrenTransformsAnimator.setDuration(duration); + mChildrenTransformsAnimator.start(); + } + + protected void reorderStarting() { + mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION); + animatePagesToNeutral(); + } + + protected boolean zoomIn(final Runnable onCompleteRunnable) { + animatePagesToCarousel(); + return super.zoomIn(onCompleteRunnable); + } + + @Override + protected void onEndReordering() { + super.onEndReordering(); + mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION); + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java index e9c90a7..b1ff049 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -62,7 +62,7 @@ public class KeyguardWidgetFrame extends FrameLayout { private float mBackgroundAlphaMultiplier = 1.0f; private Drawable mBackgroundDrawable; private Rect mBackgroundRect = new Rect(); - private static int mSmallWidgetHeight; + private int mSmallWidgetHeight; // Multiple callers may try and adjust the alpha of the frame. When a caller shows // the outlines, we give that caller control, and nobody else can fade them out. 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 800ccc0..667b2d6 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java @@ -24,7 +24,10 @@ import android.animation.TimeInterpolator; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.res.Resources; +import android.os.Handler; +import android.os.HandlerThread; import android.util.AttributeSet; +import android.util.Slog; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -33,7 +36,6 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.internal.R; - import com.android.internal.widget.LockPatternUtils; import java.util.ArrayList; @@ -46,7 +48,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit protected static float OVERSCROLL_MAX_ROTATION = 30; private static final boolean PERFORM_OVERSCROLL_ROTATION = true; - private KeyguardViewStateManager mViewStateManager; + protected KeyguardViewStateManager mViewStateManager; private LockPatternUtils mLockPatternUtils; // Related to the fading in / out background outlines @@ -58,15 +60,20 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit protected int mScreenCenter; private boolean mHasLayout = false; private boolean mHasMeasure = false; - private boolean mShowHintsOnLayout = false; + boolean showHintsAfterLayout = false; private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; + private static final String TAG = "KeyguardWidgetPager"; private int mPage = 0; private Callbacks mCallbacks; private boolean mCameraWidgetEnabled; + // Background threads to deal with persistence + private HandlerThread mBgPersistenceWorkerThread; + private Handler mBgPersistenceWorkerHandler; + public KeyguardWidgetPager(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -85,6 +92,9 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit Resources r = getResources(); mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget); + mBgPersistenceWorkerThread = new HandlerThread("KeyguardWidgetPager Persistence"); + mBgPersistenceWorkerThread.start(); + mBgPersistenceWorkerHandler = new Handler(mBgPersistenceWorkerThread.getLooper()); } public void setViewStateManager(KeyguardViewStateManager viewStateManager) { @@ -179,17 +189,28 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit public void onRemoveView(View v) { - int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); - mLockPatternUtils.removeAppWidget(appWidgetId); + final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); + mBgPersistenceWorkerHandler.post(new Runnable() { + @Override + public void run() { + mLockPatternUtils.removeAppWidget(appWidgetId); + } + }); } - public void onAddView(View v, int index) { - int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); - getVisiblePages(mTempVisiblePagesRange); - boundByReorderablePages(true, mTempVisiblePagesRange); + public void onAddView(View v, final int index) { + final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); + final int[] pagesRange = new int[mTempVisiblePagesRange.length]; + getVisiblePages(pagesRange); + boundByReorderablePages(true, pagesRange); // 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]); + mBgPersistenceWorkerHandler.post(new Runnable() { + @Override + public void run() { + mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]); + } + }); } /* @@ -226,25 +247,40 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } } - // We enforce that all children are KeyguardWidgetFrames + /** + * Use addWidget() instead. + * @deprecated + */ @Override public void addView(View child, int index) { enforceKeyguardWidgetFrame(child); super.addView(child, index); } + /** + * Use addWidget() instead. + * @deprecated + */ @Override public void addView(View child, int width, int height) { enforceKeyguardWidgetFrame(child); super.addView(child, width, height); } + /** + * Use addWidget() instead. + * @deprecated + */ @Override public void addView(View child, LayoutParams params) { enforceKeyguardWidgetFrame(child); super.addView(child, params); } + /** + * Use addWidget() instead. + * @deprecated + */ @Override public void addView(View child, int index, LayoutParams params) { enforceKeyguardWidgetFrame(child); @@ -272,7 +308,9 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit if (mViewStateManager != null) { mViewStateManager.onPageBeginMoving(); } - showOutlinesAndSidePages(); + if (!isReordering(false)) { + showOutlinesAndSidePages(); + } userActivity(); } @@ -281,17 +319,22 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit if (mViewStateManager != null) { mViewStateManager.onPageEndMoving(); } - hideOutlinesAndSidePages(); + + // In the reordering case, the pages will be faded appropriately on completion + // of the zoom in animation. + if (!isReordering(false)) { + hideOutlinesAndSidePages(); + } } - private void enablePageLayers() { + protected void enablePageLayers() { int children = getChildCount(); for (int i = 0; i < children; i++) { getWidgetPageAt(i).enableHardwareLayersForContent(); } } - private void disablePageLayers() { + protected void disablePageLayers() { int children = getChildCount(); for (int i = 0; i < children; i++) { getWidgetPageAt(i).disableHardwareLayersForContent(); @@ -414,17 +457,21 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit return true; } boolean isMusicWidgetVisible() { - // TODO: Make proper test once we have music in the list - return false; + return mViewStateManager.getTransportState() != KeyguardViewStateManager.TRANSPORT_GONE; } boolean isCameraWidgetVisible() { return mCameraWidgetEnabled; } + protected void reorderStarting() { + showOutlinesAndSidePages(); + } + @Override protected void onStartReordering() { super.onStartReordering(); - showOutlinesAndSidePages(); + enablePageLayers(); + reorderStarting(); } @Override @@ -434,7 +481,6 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } void showOutlinesAndSidePages() { - enablePageLayers(); animateOutlinesAndSidePages(true); } @@ -447,7 +493,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit showOutlinesAndSidePages(); } else { // The layout hints depend on layout being run once - mShowHintsOnLayout = true; + showHintsAfterLayout = true; } } @@ -458,16 +504,17 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit mHasLayout = false; } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (mShowHintsOnLayout) { + if (showHintsAfterLayout) { post(new Runnable() { @Override public void run() { showOutlinesAndSidePages(); } }); - mShowHintsOnLayout = false; + showHintsAfterLayout = false; } mHasLayout = true; } @@ -504,17 +551,22 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } void animateOutlinesAndSidePages(final boolean show) { + animateOutlinesAndSidePages(show, -1); + } + + void animateOutlinesAndSidePages(final boolean show, int duration) { if (mChildrenOutlineFadeAnimation != null) { mChildrenOutlineFadeAnimation.cancel(); mChildrenOutlineFadeAnimation = null; } - int count = getChildCount(); PropertyValuesHolder alpha; ArrayList<Animator> anims = new ArrayList<Animator>(); - int duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION : - CHILDREN_OUTLINE_FADE_OUT_DURATION; + if (duration == -1) { + duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION : + CHILDREN_OUTLINE_FADE_OUT_DURATION; + } int curPage = getNextPage(); for (int i = 0; i < count; i++) { @@ -541,6 +593,12 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit mChildrenOutlineFadeAnimation.setDuration(duration); mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() { @Override + public void onAnimationStart(Animator animation) { + if (show) { + enablePageLayers(); + } + } + @Override public void onAnimationEnd(Animator animation) { if (!show) { disablePageLayers(); @@ -589,9 +647,37 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit @Override public boolean onLongClick(View v) { // Disallow long pressing to reorder if the challenge is showing - if (!mViewStateManager.isChallengeShowing() && startReordering()) { + boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() && + mViewStateManager.isChallengeOverlapping(); + if (!isChallengeOverlapping && startReordering()) { return true; } return false; } + + public void removeWidget(View view) { + if (view instanceof KeyguardWidgetFrame) { + removeView(view); + } else { + // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget(). + // This supports legacy hard-coded "widgets" like KeyguardTransportControlView. + int pos = getWidgetPageIndex(view); + if (pos != -1) { + KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos); + frame.removeView(view); + removeView(frame); + } else { + Slog.w(TAG, "removeWidget() can't find:" + view); + } + } + } + + public int getWidgetPageIndex(View view) { + if (view instanceof KeyguardWidgetFrame) { + return indexOfChild(view); + } else { + // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget() + return indexOfChild((KeyguardWidgetFrame)view.getParent()); + } + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java index a207f5d..b38eb28 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java @@ -16,6 +16,10 @@ package com.android.internal.policy.impl.keyguard; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; @@ -35,8 +39,9 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo public static final int HORIZONTAL = LinearLayout.HORIZONTAL; public static final int VERTICAL = LinearLayout.VERTICAL; + protected static final int ANIMATE_BOUNCE_DURATION = 750; - private View mChallengeView; + private KeyguardSecurityContainer mChallengeView; private View mUserSwitcherView; private View mScrimView; private OnBouncerStateChangedListener mBouncerListener; @@ -87,7 +92,19 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo if (mIsBouncing) return; mIsBouncing = true; if (mScrimView != null) { - mScrimView.setVisibility(GONE); + if (mChallengeView != null) { + mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION); + } + + Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f); + anim.setDuration(ANIMATE_BOUNCE_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mScrimView.setVisibility(VISIBLE); + } + }); + anim.start(); } if (mBouncerListener != null) { mBouncerListener.onBouncerStateChanged(true); @@ -99,7 +116,19 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo if (!mIsBouncing) return; mIsBouncing = false; if (mScrimView != null) { - mScrimView.setVisibility(GONE); + if (mChallengeView != null) { + mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION); + } + + Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f); + anim.setDuration(ANIMATE_BOUNCE_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mScrimView.setVisibility(INVISIBLE); + } + }); + anim.start(); } if (mBouncerListener != null) { mBouncerListener.onBouncerStateChanged(false); @@ -131,7 +160,8 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo mScrimView.setOnClickListener(null); } mScrimView = scrim; - mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE); + mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f); + mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE); mScrimView.setFocusable(true); mScrimView.setOnClickListener(mScrimClickListener); } @@ -165,7 +195,11 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo throw new IllegalStateException( "There may only be one child of type challenge"); } - mChallengeView = child; + if (!(child instanceof KeyguardSecurityContainer)) { + throw new IllegalArgumentException( + "Challenge must be a KeyguardSecurityContainer"); + } + mChallengeView = (KeyguardSecurityContainer) child; } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) { if (mUserSwitcherView != null) { throw new IllegalStateException( diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java index 060cc03..ca36007 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java @@ -22,7 +22,9 @@ import android.text.SpannableStringBuilder; import android.text.style.TextAppearanceSpan; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; +import android.view.accessibility.AccessibilityManager; import android.widget.Button; import android.widget.TextView; @@ -72,6 +74,7 @@ public class NumPadKey extends Button { setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0)); setOnClickListener(mListener); + setOnHoverListener(new LiftToActivateListener(context)); mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled(); @@ -113,4 +116,45 @@ public class NumPadKey extends Button { | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } } + + /** + * Hover listener that implements lift-to-activate interaction for + * accessibility. May be added to multiple views. + */ + static class LiftToActivateListener implements View.OnHoverListener { + /** Manager used to query accessibility enabled state. */ + private final AccessibilityManager mAccessibilityManager; + + public LiftToActivateListener(Context context) { + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + } + + @Override + public boolean onHover(View v, MotionEvent event) { + // When touch exploration is turned on, lifting a finger while + // inside the view bounds should perform a click action. + if (mAccessibilityManager.isEnabled() + && mAccessibilityManager.isTouchExplorationEnabled()) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + // Lift-to-type temporarily disables double-tap + // activation. + v.setClickable(false); + break; + case MotionEvent.ACTION_HOVER_EXIT: + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop()) + && (x < v.getWidth() - v.getPaddingRight()) + && (y < v.getHeight() - v.getPaddingBottom())) { + v.performClick(); + } + v.setClickable(true); + break; + } + } + return false; + } + } } 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 657a31f..c93b11a 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java @@ -1995,7 +1995,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } // "Zooms out" the PagedView to reveal more side pages - boolean zoomOut() { + protected boolean zoomOut() { if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { mZoomInOutAnim.cancel(); } @@ -2072,15 +2072,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // If we haven't flung-to-delete the current child, then we just animate the drag view // back into position + final Runnable onCompleteRunnable = new Runnable() { + @Override + public void run() { + onEndReordering(); + } + }; if (!mIsFlingingToDelete) { mPostReorderingPreZoomInRunnable = new Runnable() { public void run() { - Runnable onCompleteRunnable = new Runnable() { - @Override - public void run() { - onEndReordering(); - } - }; zoomIn(onCompleteRunnable); }; }; @@ -2091,11 +2091,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc snapToPage(indexOfChild(mDragView), 0); // Animate the drag view back to the front position animateDragViewToOriginalPosition(); + } else { + zoomIn(onCompleteRunnable); } } // "Zooms in" the PagedView to highlight the current page - boolean zoomIn(final Runnable onCompleteRunnable) { + protected boolean zoomIn(final Runnable onCompleteRunnable) { if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { mZoomInOutAnim.cancel(); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java index ec6472f..7760279 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java @@ -24,4 +24,8 @@ public interface SecurityMessageDisplay { public void setMessage(int resId, boolean important, Object... formatArgs); public void setTimeout(int timeout_ms); + + public void showBouncer(int animationDuration); + + public void hideBouncer(int animationDuration); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java index 35eccbb..2e735a0 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java @@ -16,11 +16,15 @@ package com.android.internal.policy.impl.keyguard; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatProperty; @@ -61,10 +65,12 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout private Drawable mHandleDrawable; private Drawable mFrameDrawable; private Drawable mDragIconDrawable; + private boolean mEdgeCaptured; // Initialized during measurement from child layoutparams private View mChallengeView; private View mScrimView; + private View mWidgetsView; // Range: 0 (fully hidden) to 1 (fully visible) private float mChallengeOffset = 1.f; @@ -110,9 +116,14 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout float mHandleAlpha; float mFrameAlpha; + float mFrameAnimationTarget = Float.MIN_VALUE; private ObjectAnimator mHandleAnimation; private ObjectAnimator mFrameAnimation; + private final Rect mTempRect = new Rect(); + + private boolean mHasGlowpad; + static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA = new FloatProperty<SlidingChallengeLayout>("handleAlpha") { @Override @@ -237,12 +248,12 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mMinVelocity = vc.getScaledMinimumFlingVelocity(); mMaxVelocity = vc.getScaledMaximumFlingVelocity(); - mDragHandleEdgeSlop = getResources().getDimensionPixelSize( - R.dimen.kg_edge_swipe_region_size); + final Resources res = getResources(); + mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - final float density = getResources().getDisplayMetrics().density; + final float density = res.getDisplayMetrics().density; // top half of the lock icon, plus another 25% to be sure mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f); @@ -251,7 +262,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f); // how much space to account for in the handle when closed - mChallengeBottomBound = mDragHandleClosedBelow; + mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding); setWillNotDraw(false); } @@ -293,21 +304,43 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mHandleAnimation.start(); } - void animateFrame(boolean visible, boolean full) { + void animateFrame(final boolean visible, final boolean full) { if (mFrameDrawable == null) return; - if (mFrameAnimation != null) { + final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f; + if (mFrameAnimation != null && targetAlpha != mFrameAnimationTarget) { mFrameAnimation.cancel(); - mFrameAnimation = null; + mFrameAnimationTarget = Float.MIN_VALUE; } - final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f; - if (targetAlpha == mFrameAlpha) { + if (targetAlpha == mFrameAlpha || targetAlpha == mFrameAnimationTarget) { return; } + mFrameAnimationTarget = targetAlpha; mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha); mFrameAnimation.setInterpolator(sHandleFadeInterpolator); mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION); + mFrameAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mFrameAnimationTarget = Float.MIN_VALUE; + + if (!visible && full && mChallengeView != null) { + // Mess with padding/margin to remove insets on the bouncer frame. + mChallengeView.setPadding(0, 0, 0, 0); + LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams(); + lp.leftMargin = lp.rightMargin = getChallengeMargin(true); + mChallengeView.setLayoutParams(lp); + } + mFrameAnimation = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + mFrameAnimationTarget = Float.MIN_VALUE; + mFrameAnimation = null; + } + }); mFrameAnimation.start(); } @@ -370,7 +403,9 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mScrollState = state; animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing); - animateFrame(false , false); + if (!mIsBouncing) { + animateFrame(false, false); + } if (mScrollListener != null) { mScrollListener.onScrollStateChanged(state); } @@ -380,6 +415,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout void completeChallengeScroll() { setChallengeShowing(mChallengeOffset != 0); setScrollState(SCROLL_STATE_IDLE); + mChallengeView.setLayerType(LAYER_TYPE_NONE, null); } void setScrimView(View scrim) { @@ -461,7 +497,22 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout if (mScrimView != null) { mScrimView.setVisibility(VISIBLE); } + + // Mess with padding/margin to inset the bouncer frame. + // We have more space available to us otherwise. + if (mChallengeView != null) { + if (mFrameDrawable == null || !mFrameDrawable.getPadding(mTempRect)) { + mTempRect.set(0, 0, 0, 0); + } + mChallengeView.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, + mTempRect.bottom); + final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams(); + lp.leftMargin = lp.rightMargin = getChallengeMargin(false); + mChallengeView.setLayoutParams(lp); + } + animateFrame(true, true); + if (mBouncerListener != null) { mBouncerListener.onBouncerStateChanged(true); } @@ -470,17 +521,26 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout @Override public void hideBouncer() { if (!mIsBouncing) return; - setChallengeShowing(false); + showChallenge(false); mIsBouncing = false; if (mScrimView != null) { mScrimView.setVisibility(GONE); } - animateFrame(false, false); + animateFrame(false, true); if (mBouncerListener != null) { mBouncerListener.onBouncerStateChanged(false); } } + private int getChallengeMargin(boolean expanded) { + return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop; + } + + private float getChallengeAlpha() { + float x = mChallengeOffset - 1; + return x * x * x + 1.f; + } + @Override public void requestDisallowInterceptTouchEvent(boolean allowIntercept) { // We'll intercept whoever we feel like! ...as long as it isn't a challenge view. @@ -495,8 +555,6 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } mVelocityTracker.addMovement(ev); - //Log.v(TAG, "onIntercept: " + ev); - final int action = ev.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: @@ -518,13 +576,15 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout if (!mIsBouncing && (isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) || - (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) && + (isInChallengeView(x, y) && + (mScrollState == SCROLL_STATE_SETTLING || !mChallengeShowing))) && mActivePointerId == INVALID_POINTER) { mActivePointerId = ev.getPointerId(i); mGestureStartX = x; mGestureStartY = y; mGestureStartChallengeBottom = getChallengeBottom(); mDragging = true; + mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null); } else if (isInChallengeView(x, y)) { mBlockDrag = true; } @@ -601,6 +661,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mActivePointerId = ev.getPointerId(i); mGestureStartChallengeBottom = getChallengeBottom(); mDragging = true; + mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null); break; } } @@ -631,6 +692,52 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } /** + * The lifecycle of touch events is subtle and it's very easy to do something + * that will cause bugs that will be nasty to track when overriding this method. + * Normally one should always override onInterceptTouchEvent instead. + * + * To put it another way, don't try this at home. + */ + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + final int action = ev.getActionMasked(); + boolean handled = false; + if (action == MotionEvent.ACTION_DOWN) { + // Defensive programming: if we didn't get the UP or CANCEL, reset anyway. + mEdgeCaptured = false; + } + if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) { + // Normally we would need to do a lot of extra stuff here. + // We can only get away with this because we haven't padded in + // the widget pager or otherwise transformed it during layout. + // We also don't support things like splitting MotionEvents. + + // We set handled to captured even if dispatch is returning false here so that + // we don't send a different view a busted or incomplete event stream. + handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev); + } + + if (!handled && !mEdgeCaptured) { + handled = super.dispatchTouchEvent(ev); + } + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mEdgeCaptured = false; + } + + return handled; + } + + private boolean isEdgeSwipeBeginEvent(MotionEvent ev) { + if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { + return false; + } + + final float x = ev.getX(); + return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop; + } + + /** * We only want to add additional vertical space to the drag handle when the panel is fully * closed. */ @@ -699,14 +806,24 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } // We're going to play silly games with the frame's background drawable later. mFrameDrawable = mChallengeView.getBackground(); + + if (!mHasLayout) { + // Set up the margin correctly based on our content for the first run. + mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null; + lp.leftMargin = lp.rightMargin = getChallengeMargin(true); + } } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { setScrimView(child); + } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { + mWidgetsView = child; } + if (child.getVisibility() == GONE) continue; } - // We want to measure the challenge view first, for various reasons that I'd rather - // not get into here. + // We want to measure the challenge view first, since the KeyguardWidgetPager + // needs to do things its measure pass that are dependent on the challenge view + // having been measured. if (mChallengeView != null) { measureChildWithMargins(mChallengeView, widthSpec, 0, heightSpec, 0); } @@ -750,7 +867,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // 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.setAlpha(getChallengeAlpha()); child.layout(left, bottom - childHeight, left + childWidth, bottom); } else { // Non-challenge views lay out from the upper left, layered. @@ -828,7 +945,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } if (mDragIconDrawable != null) { - final int closedTop = getLayoutBottom() - mChallengeBottomBound; + final int closedTop = getLayoutBottom() - mDragHandleClosedBelow; final int iconWidth = mDragIconDrawable.getIntrinsicWidth(); final int iconHeight = mDragIconDrawable.getIntrinsicHeight(); final int iconLeft = (challengeLeft + challengeRight - iconWidth) / 2; @@ -886,7 +1003,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout mChallengeView.layout(mChallengeView.getLeft(), bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom); - mChallengeView.setAlpha(offset / 2 + 0.5f); + mChallengeView.setAlpha(getChallengeAlpha()); if (mScrollListener != null) { mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop()); } @@ -978,6 +1095,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout public static final int CHILD_TYPE_NONE = 0; public static final int CHILD_TYPE_CHALLENGE = 2; public static final int CHILD_TYPE_SCRIM = 4; + public static final int CHILD_TYPE_WIDGETS = 5; public LayoutParams() { this(MATCH_PARENT, WRAP_CONTENT); diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java index 6ff33d7..69ccbc7 100755 --- a/services/java/com/android/server/BluetoothManagerService.java +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -53,6 +53,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; private static final String EXTRA_ACTION="action"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind @@ -174,7 +175,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Enable if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); enableHelper(); - } else if (!isNameAndAddressSet()) { + } + + if (!isNameAndAddressSet()) { //Sync the Bluetooth name and address from the Bluetooth Adapter if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); getNameAndAddress(); @@ -222,11 +225,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void loadStoredNameAndAddress() { if (DBG) Log.d(TAG, "Loading stored name and address"); + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_address_validation) && + Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) { + // if the valid flag is not set, don't load the address and name + if (DBG) Log.d(TAG, "invalid bluetooth name and address stored"); + return; + } mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); - if (mName == null || mAddress == null) { - if (DBG) Log.d(TAG, "Name or address not cached..."); - } + if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); } /** @@ -249,6 +257,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); } + + if ((name != null) && (address != null)) { + Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1); + } } public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ @@ -560,8 +572,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } case MESSAGE_SAVE_NAME_AND_ADDRESS: { + boolean unbind = false; if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS"); synchronized(mConnection) { + if (!mEnable && mBluetooth != null) { + try { + mBluetooth.enable(); + } catch (RemoteException e) { + Log.e(TAG,"Unable to call enable()",e); + } + } + } + if (mBluetooth != null) waitForOnOff(true, false); + synchronized(mConnection) { if (mBluetooth != null) { String name = null; String address = null; @@ -575,7 +598,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (name != null && address != null) { storeNameAndAddress(name,address); if (mConnection.isGetNameAddressOnly()) { - unbindAndFinish(); + unbind = true; } } else { if (msg.arg1 < MAX_SAVE_RETRIES) { @@ -586,10 +609,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { Log.w(TAG,"Maximum name/address remote retrieval retry exceeded"); if (mConnection.isGetNameAddressOnly()) { - unbindAndFinish(); + unbind = true; } } } + if (!mEnable) { + try { + mBluetooth.disable(); + } catch (RemoteException e) { + Log.e(TAG,"Unable to call disable()",e); + } + } } else { // rebind service by Request GET NAME AND ADDRESS // if service is unbinded by disable or @@ -598,6 +628,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessage(getMsg); } } + if (!mEnable && mBluetooth != null) waitForOnOff(false, true); + if (unbind) { + unbindAndFinish(); + } break; } case MESSAGE_ENABLE: @@ -677,14 +711,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Inform BluetoothAdapter instances that service is up sendBluetoothServiceUpCallback(); - //Check if name and address is loaded if not get it first. - if (!isNameAndAddressSet()) { - try { - storeNameAndAddress(mBluetooth.getName(), - mBluetooth.getAddress()); - } catch (RemoteException e) {Log.e(TAG, "", e);}; - } - //Do enable request try { if (mQuietEnable == false) { @@ -873,14 +899,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothServiceUpCallback(); } - //Check if name and address is loaded if not get it first. - if (!isNameAndAddressSet()) { - try { - if (DBG) Log.d(TAG,"Getting and storing Bluetooth name and address prior to enable."); - storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress()); - } catch (RemoteException e) {Log.e(TAG, "", e);}; - } - //Enable bluetooth try { if (!mQuietEnable) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 4a54efe..0e171cd 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -889,7 +889,7 @@ public class NotificationManagerService extends INotificationManager.Stub final boolean isSystemNotification = ("android".equals(pkg)); userId = ActivityManager.handleIncomingUser(callingPid, - callingUid, userId, true, true, "enqueueNotification", pkg); + callingUid, userId, true, false, "enqueueNotification", pkg); final UserHandle user = new UserHandle(userId); // Limit the number of notifications that any given package except the android @@ -1287,7 +1287,7 @@ public class NotificationManagerService extends INotificationManager.Stub public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { checkCallerIsSystemOrSameApp(pkg); userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, true, true, "cancelNotificationWithTag", pkg); + Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); // Don't allow client applications to cancel foreground service notis. cancelNotification(pkg, tag, id, 0, Binder.getCallingUid() == Process.SYSTEM_UID @@ -1298,7 +1298,7 @@ public class NotificationManagerService extends INotificationManager.Stub checkCallerIsSystemOrSameApp(pkg); userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, true, true, "cancelAllNotifications", pkg); + Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); // Calling from user space, don't allow the canceling of actively // running foreground services. diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index a02fc8d..82dbf54 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -466,10 +466,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (Intent.ACTION_USER_REMOVED.equals(action)) { onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); - } else if (Intent.ACTION_USER_STOPPING.equals(action)) { - onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, - UserHandle.USER_NULL)); } + // TODO: Race condition causing problems when cleaning up on stopping a user. + // Comment this out for now. + // else if (Intent.ACTION_USER_STOPPING.equals(action)) { + // onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + // UserHandle.USER_NULL)); + // } } }, userFilter); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 5722326..d2cd646 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -4575,7 +4575,7 @@ public final class ActivityManagerService extends ActivityManagerNative int callingUid = Binder.getCallingUid(); int origUserId = userId; userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, - type == ActivityManager.INTENT_SENDER_BROADCAST, true, + type == ActivityManager.INTENT_SENDER_BROADCAST, false, "getIntentSender", null); if (origUserId == UserHandle.USER_CURRENT) { // We don't want to evaluate this until the pending intent is diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java index 2445b98..ca94d04 100644 --- a/services/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/java/com/android/server/wm/AppWindowAnimator.java @@ -279,22 +279,22 @@ public class AppWindowAnimator { return isAnimating; } - void dump(PrintWriter pw, String prefix) { - if (freezingScreen) { - pw.print(prefix); pw.print(" freezingScreen="); pw.println(freezingScreen); - } + void dump(PrintWriter pw, String prefix, boolean dumpAll) { + pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); + pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator); + pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); + pw.print(" allDrawn="); pw.print(allDrawn); + pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); if (animating || animation != null) { pw.print(prefix); pw.print("animating="); pw.print(animating); - pw.print(" animation="); pw.println(animation); + pw.print(" animInitialized="); pw.println(animInitialized); + pw.print(prefix); pw.print("animation="); pw.println(animation); } if (hasTransformation) { pw.print(prefix); pw.print("XForm: "); transformation.printShortString(pw); pw.println(); } - if (animLayerAdjustment != 0) { - pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment); - } if (thumbnail != null) { pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); pw.print(" x="); pw.print(thumbnailX); @@ -304,6 +304,11 @@ public class AppWindowAnimator { pw.print(prefix); pw.print("thumbnailTransformation="); pw.println(thumbnailTransformation.toShortString()); } + for (int i=0; i<mAllAppWinAnimators.size(); i++) { + WindowStateAnimator wanim = mAllAppWinAnimators.get(i); + pw.print(prefix); pw.print("App Win Anim #"); pw.print(i); + pw.print(": "); pw.println(wanim); + } } // This is an animation that does nothing: it just immediately finishes diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 9a62482..c8d9cc3 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -18,12 +18,14 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.TimeUtils; import android.view.Display; import android.view.Surface; import android.view.WindowManagerPolicy; import android.view.animation.Animation; import com.android.server.wm.WindowManagerService.AppWindowAnimParams; +import com.android.server.wm.WindowManagerService.LayoutFields; import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams; import java.io.PrintWriter; @@ -197,6 +199,15 @@ public class WindowAnimator { mWallpaperTokens = new ArrayList<WindowToken>(layoutToAnim.mWallpaperTokens); } + if (WindowManagerService.DEBUG_WALLPAPER_LIGHT) { + if (mWallpaperTarget != layoutToAnim.mWallpaperTarget + || mLowerWallpaperTarget != layoutToAnim.mLowerWallpaperTarget + || mUpperWallpaperTarget != layoutToAnim.mUpperWallpaperTarget) { + Slog.d(TAG, "Updating anim wallpaper: target=" + mWallpaperTarget + + " lower=" + mLowerWallpaperTarget + " upper=" + + mUpperWallpaperTarget); + } + } mWallpaperTarget = layoutToAnim.mWallpaperTarget; mWpAppAnimator = mWallpaperTarget == null ? null : mWallpaperTarget.mAppToken == null @@ -735,46 +746,144 @@ public class WindowAnimator { return dimParams != null && dimParams.mDimWinAnimator == winAnimator; } + static String bulkUpdateParamsToString(int bulkUpdateParams) { + StringBuilder builder = new StringBuilder(128); + if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) { + builder.append(" UPDATE_ROTATION"); + } + if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) { + builder.append(" WALLPAPER_MAY_CHANGE"); + } + if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) { + builder.append(" FORCE_HIDING_CHANGED"); + } + if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) != 0) { + builder.append(" ORIENTATION_CHANGE_COMPLETE"); + } + if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) { + builder.append(" TURN_ON_SCREEN"); + } + return builder.toString(); + } + public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) { - if (dumpAll) { - if (mWindowDetachedWallpaper != null) { - pw.print(prefix); pw.print("mWindowDetachedWallpaper="); - pw.println(mWindowDetachedWallpaper); - } - pw.print(prefix); pw.print("mAnimTransactionSequence="); - pw.print(mAnimTransactionSequence); - pw.println(" mForceHiding=" + forceHidingToString()); - for (int i = 0; i < mDisplayContentsAnimators.size(); i++) { - pw.print(prefix); pw.print("DisplayContentsAnimator #"); - pw.println(mDisplayContentsAnimators.keyAt(i)); - DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); - final String subPrefix = " " + prefix; - final String subSubPrefix = " " + subPrefix; - if (displayAnimator.mWindowAnimationBackgroundSurface != null) { - pw.println(subPrefix + "mWindowAnimationBackgroundSurface:"); - displayAnimator.mWindowAnimationBackgroundSurface.printTo(subSubPrefix, pw); - } - if (displayAnimator.mDimAnimator != null) { - pw.println(subPrefix + "mDimAnimator:"); - displayAnimator.mDimAnimator.printTo(subSubPrefix, pw); - } else { - pw.println(subPrefix + "no DimAnimator "); - } - if (displayAnimator.mDimParams != null) { - pw.println(subPrefix + "mDimParams:"); - displayAnimator.mDimParams.printTo(subSubPrefix, pw); + final String subPrefix = " " + prefix; + final String subSubPrefix = " " + subPrefix; + + boolean needSep = false; + if (mAppAnimators.size() > 0) { + needSep = true; + pw.println(" App Animators:"); + for (int i=mAppAnimators.size()-1; i>=0; i--) { + AppWindowAnimator anim = mAppAnimators.get(i); + pw.print(prefix); pw.print("App Animator #"); pw.print(i); + pw.print(' '); pw.print(anim); + if (dumpAll) { + pw.println(':'); + anim.dump(pw, subPrefix, dumpAll); } else { - pw.println(subPrefix + "no DimParams "); + pw.println(); } - if (displayAnimator.mScreenRotationAnimation != null) { - pw.println(subPrefix + "mScreenRotationAnimation:"); - displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw); + } + } + if (mWallpaperTokens.size() > 0) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.print(prefix); pw.println("Wallpaper tokens:"); + for (int i=mWallpaperTokens.size()-1; i>=0; i--) { + WindowToken token = mWallpaperTokens.get(i); + pw.print(prefix); pw.print("Wallpaper #"); pw.print(i); + pw.print(' '); pw.print(token); + if (dumpAll) { + pw.println(':'); + token.dump(pw, subPrefix); } else { - pw.print(subPrefix + "no ScreenRotationAnimation "); + pw.println(); } } + } + + if (needSep) { pw.println(); } + for (int i = 0; i < mDisplayContentsAnimators.size(); i++) { + pw.print(prefix); pw.print("DisplayContentsAnimator #"); + pw.print(mDisplayContentsAnimators.keyAt(i)); + pw.println(":"); + DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); + for (int j=0; j<displayAnimator.mWinAnimators.size(); j++) { + WindowStateAnimator wanim = displayAnimator.mWinAnimators.get(j); + pw.print(subPrefix); pw.print("Window #"); pw.print(j); + pw.print(": "); pw.println(wanim); + } + if (displayAnimator.mWindowAnimationBackgroundSurface != null) { + if (dumpAll || displayAnimator.mWindowAnimationBackgroundSurface.mDimShown) { + pw.print(subPrefix); pw.println("mWindowAnimationBackgroundSurface:"); + displayAnimator.mWindowAnimationBackgroundSurface.printTo(subSubPrefix, pw); + } + } + if (displayAnimator.mDimAnimator != null) { + if (dumpAll || displayAnimator.mDimAnimator.mDimShown) { + pw.print(subPrefix); pw.println("mDimAnimator:"); + displayAnimator.mDimAnimator.printTo(subSubPrefix, pw); + } + } else if (dumpAll) { + pw.print(subPrefix); pw.println("no DimAnimator "); + } + if (displayAnimator.mDimParams != null) { + pw.print(subPrefix); pw.println("mDimParams:"); + displayAnimator.mDimParams.printTo(subSubPrefix, pw); + } else if (dumpAll) { + pw.print(subPrefix); pw.println("no DimParams "); + } + if (displayAnimator.mScreenRotationAnimation != null) { + pw.print(subPrefix); pw.println("mScreenRotationAnimation:"); + displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw); + } else if (dumpAll) { + pw.print(subPrefix); pw.println("no ScreenRotationAnimation "); + } + } + + pw.println(); + + if (dumpAll) { + pw.print(prefix); pw.print("mAnimTransactionSequence="); + pw.print(mAnimTransactionSequence); + pw.print(" mForceHiding="); pw.println(forceHidingToString()); + pw.print(prefix); pw.print("mCurrentTime="); + pw.println(TimeUtils.formatUptime(mCurrentTime)); + pw.print(prefix); pw.print("mDw="); + pw.print(mDw); pw.print(" mDh="); pw.print(mDh); + pw.print(" mInnerDw="); pw.print(mInnerDw); + pw.print(" mInnerDh="); pw.println(mInnerDh); + } + if (mBulkUpdateParams != 0) { + pw.print(prefix); pw.print("mBulkUpdateParams=0x"); + pw.print(Integer.toHexString(mBulkUpdateParams)); + pw.println(bulkUpdateParamsToString(mBulkUpdateParams)); + } + if (mPendingActions != 0) { + pw.print(prefix); pw.print("mPendingActions=0x"); + pw.println(Integer.toHexString(mPendingActions)); + } + if (mWindowDetachedWallpaper != null) { + pw.print(prefix); pw.print("mWindowDetachedWallpaper="); + pw.println(mWindowDetachedWallpaper); + } + pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget); + pw.print(prefix); pw.print("mWpAppAnimator="); pw.println(mWpAppAnimator); + if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) { + pw.print(prefix); pw.print("mLowerWallpaperTarget="); + pw.println(mLowerWallpaperTarget); + pw.print(prefix); pw.print("mUpperWallpaperTarget="); + pw.println(mUpperWallpaperTarget); + } + if (mUniverseBackground != null) { + pw.print(prefix); pw.print("mUniverseBackground="); pw.print(mUniverseBackground); + pw.print(" mAboveUniverseLayer="); pw.println(mAboveUniverseLayer); + } } void clearPendingActions() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 637f2de..137c8ee 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -2773,7 +2773,7 @@ public class WindowManagerService extends IWindowManager.Stub // TODO: Remove once b/7094175 is fixed || ((String)win.mAttrs.getTitle()).contains("Keyguard") ) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility - + " " + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); + + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; @@ -3003,6 +3003,10 @@ public class WindowManagerService extends IWindowManager.Stub } mInputMonitor.updateInputWindowsLw(true /*force*/); + + if (DEBUG_LAYOUT) { + Slog.v(TAG, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString()); + } } if (configChanged) { @@ -8379,7 +8383,8 @@ public class WindowManagerService extends IWindowManager.Stub // windows, since that means "perform layout as normal, // just don't display"). if (!gone || !win.mHaveFrame || win.mLayoutNeeded - || win.isConfigChanged() + || ((win.mAttrs.type == TYPE_KEYGUARD || win.mAttrs.type == TYPE_WALLPAPER) && + win.isConfigChanged()) || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { if (!win.mLayoutAttached) { if (initial) { @@ -9301,6 +9306,8 @@ public class WindowManagerService extends IWindowManager.Stub Log.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { Surface.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } final WindowList defaultWindows = defaultDisplay.getWindowList(); @@ -10322,6 +10329,11 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.dump(" ", pw, args); } + void dumpAnimatorLocked(PrintWriter pw, String[] args, boolean dumpAll) { + pw.println("WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)"); + mAnimator.dumpLocked(pw, " ", dumpAll); + } + void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); if (mTokenMap.size() > 0) { @@ -10598,7 +10610,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow); } pw.print(" mWallpaperTarget="); pw.println(mWallpaperTarget); - if (mLowerWallpaperTarget != null && mUpperWallpaperTarget != null) { + if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) { pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget); pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget); } @@ -10682,8 +10694,32 @@ public class WindowManagerService extends IWindowManager.Stub } pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition); pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation); - pw.println(" Window Animator:"); - mAnimator.dumpLocked(pw, " ", dumpAll); + pw.println(" mLayoutToAnim:"); + pw.print(" mParamsModified="); pw.print(mLayoutToAnim.mParamsModified); + pw.print(" mAnimationScheduled="); pw.print(mLayoutToAnim.mAnimationScheduled); + pw.print(" mChanges=0x"); + pw.println(Long.toHexString(mLayoutToAnim.mChanges)); + pw.print(" mWallpaperTarget="); pw.println(mLayoutToAnim.mWallpaperTarget); + if (mLayoutToAnim.mLowerWallpaperTarget != null + || mLayoutToAnim.mUpperWallpaperTarget != null) { + pw.print(" mLowerWallpaperTarget="); + pw.println(mLayoutToAnim.mLowerWallpaperTarget); + pw.print(" mUpperWallpaperTarget="); + pw.println(mLayoutToAnim.mUpperWallpaperTarget); + } + for (int i=0; i<mLayoutToAnim.mWinAnimatorLists.size(); i++) { + pw.print(" Win Animator List #"); + pw.print(mLayoutToAnim.mWinAnimatorLists.keyAt(i)); pw.println(":"); + WinAnimatorList wanim = mLayoutToAnim.mWinAnimatorLists.valueAt(i); + for (int wi=0; wi<wanim.size(); wi++) { + pw.print(" "); pw.println(wanim.get(wi)); + } + } + for (int i=0; i<mLayoutToAnim.mWallpaperTokens.size(); i++) { + pw.print(" Wallpaper Token #"); pw.print(i); pw.print(": "); + pw.println(mLayoutToAnim.mWallpaperTokens.get(i)); + } + // XXX also need to print mDimParams and mAppWindowAnimParams. I am lazy. } } @@ -10793,6 +10829,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(" cmd may be one of:"); pw.println(" l[astanr]: last ANR information"); pw.println(" p[policy]: policy state"); + pw.println(" a[animator]: animator state"); pw.println(" s[essions]: active sessions"); pw.println(" t[okens]: token list"); pw.println(" w[indows]: window list"); @@ -10822,6 +10859,11 @@ public class WindowManagerService extends IWindowManager.Stub dumpPolicyLocked(pw, args, true); } return; + } else if ("animator".equals(cmd) || "a".equals(cmd)) { + synchronized(mWindowMap) { + dumpAnimatorLocked(pw, args, true); + } + return; } else if ("sessions".equals(cmd) || "s".equals(cmd)) { synchronized(mWindowMap) { dumpSessionsLocked(pw, true); @@ -10867,6 +10909,11 @@ public class WindowManagerService extends IWindowManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } + dumpAnimatorLocked(pw, args, dumpAll); + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } dumpSessionsLocked(pw, dumpAll); pw.println(); if (dumpAll) { diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index c195f45..e1cc58f 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -250,7 +250,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Used to improve performance of toString() String mStringNameCache; CharSequence mLastTitle; - boolean mWasPaused; + boolean mWasExiting; final WindowStateAnimator mWinAnimator; @@ -282,7 +282,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; if (WindowManagerService.localLOGV) Slog.v( TAG, "Window " + this + " client=" + c.asBinder() - + " token=" + token + " (" + mAttrs.token + ")"); + + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); try { c.asBinder().linkToDeath(deathRecipient, 0); } catch (RemoteException e) { @@ -1206,7 +1206,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(" visible="); mLastVisibleInsets.printShortString(pw); pw.println(); } - mWinAnimator.dump(pw, prefix, dumpAll); + pw.print(prefix); pw.print(mWinAnimator); pw.println(":"); + mWinAnimator.dump(pw, prefix + " ", dumpAll); if (mExiting || mRemoveOnExit || mDestroying || mRemoved) { pw.print(prefix); pw.print("mExiting="); pw.print(mExiting); pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); @@ -1241,9 +1242,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { @Override public String toString() { if (mStringNameCache == null || mLastTitle != mAttrs.getTitle() - || mWasPaused != mToken.paused) { + || mWasExiting != mExiting) { mLastTitle = mAttrs.getTitle(); - mWasPaused = mToken.paused; + mWasExiting = mExiting; mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) + " u" + UserHandle.getUserId(mSession.mUid) + " " + mLastTitle + (mExiting ? " EXITING}" : "}"); diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 2bfefe1..85f087f 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -1604,10 +1604,11 @@ class WindowStateAnimator { @Override public String toString() { - StringBuffer sb = new StringBuffer("WindowStateAnimator ("); - sb.append(mWin.mLastTitle + "): "); - sb.append("mSurface " + mSurface); - sb.append(", mAnimation " + mAnimation); + StringBuffer sb = new StringBuffer("WindowStateAnimator{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); + sb.append(mWin.mAttrs.getTitle()); + sb.append('}'); return sb.toString(); } } |
