Merge "Require that persistable Uri permissions be taken." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 5362fdf..c2dfa92 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -71,6 +71,7 @@
field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
+ field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INTERNET = "android.permission.INTERNET";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
@@ -132,6 +133,7 @@
field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
field public static final java.lang.String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
+ field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index dd2e006..45a38be 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -42,4 +42,5 @@
oneway void setCurrentUser(int userId);
oneway void showAssistant();
oneway void dispatch(in MotionEvent event);
+ oneway void launchCamera();
}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 356401c..e30c1ff 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -31,7 +31,6 @@
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -52,14 +51,12 @@
/** Temporary rectangle used for computing line bounds. */
private final RectF mLineBounds = new RectF();
- /** Temporary array used for computing line wrapping. */
- private float[] mTextWidths;
-
/** Reusable string builder used for holding text. */
private final StringBuilder mText = new StringBuilder();
- private final StringBuilder mBreakText = new StringBuilder();
- private TextPaint mPaint;
+ private Alignment mAlignment;
+ private TextPaint mTextPaint;
+ private Paint mPaint;
private int mForegroundColor;
private int mBackgroundColor;
@@ -122,11 +119,12 @@
mShadowOffsetX = res.getDimension(com.android.internal.R.dimen.subtitle_shadow_offset);
mShadowOffsetY = mShadowOffsetX;
- final TextPaint paint = new TextPaint();
- paint.setAntiAlias(true);
- paint.setSubpixelText(true);
+ mTextPaint = new TextPaint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setSubpixelText(true);
- mPaint = paint;
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
setText(text);
setTextSize(textSize);
@@ -174,21 +172,30 @@
public void setTextSize(float size) {
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
final float pixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics);
- if (mPaint.getTextSize() != size) {
- mHasMeasurements = false;
+ if (mTextPaint.getTextSize() != size) {
+ mTextPaint.setTextSize(size);
mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
- mPaint.setTextSize(size);
- requestLayout();
+ mHasMeasurements = false;
+ forceLayout();
}
}
public void setTypeface(Typeface typeface) {
- if (mPaint.getTypeface() != typeface) {
- mHasMeasurements = false;
- mPaint.setTypeface(typeface);
+ if (mTextPaint.getTypeface() != typeface) {
+ mTextPaint.setTypeface(typeface);
- requestLayout();
+ mHasMeasurements = false;
+ forceLayout();
+ }
+ }
+
+ public void setAlignment(Alignment textAlignment) {
+ if (mAlignment != textAlignment) {
+ mAlignment = textAlignment;
+
+ mHasMeasurements = false;
+ forceLayout();
}
}
@@ -222,63 +229,19 @@
}
// Account for padding.
- final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX;
+ final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX * 2;
maxWidth -= paddingX;
-
if (maxWidth <= 0) {
return false;
}
- final TextPaint paint = mPaint;
- final CharSequence text = mText;
- final int textLength = text.length();
- if (mTextWidths == null || mTextWidths.length < textLength) {
- mTextWidths = new float[textLength];
- }
-
- final float[] textWidths = mTextWidths;
- paint.getTextWidths(text, 0, textLength, textWidths);
-
- // Compute total length.
- float runLength = 0;
- for (int i = 0; i < textLength; i++) {
- runLength += textWidths[i];
- }
-
- final int lineCount = (int) (runLength / maxWidth) + 1;
- final int lineLength = (int) (runLength / lineCount);
-
- // Build line break buffer.
- final StringBuilder breakText = mBreakText;
- breakText.setLength(0);
-
- int line = 0;
- int lastBreak = 0;
- int maxRunLength = 0;
- runLength = 0;
- for (int i = 0; i < textLength; i++) {
- if (runLength > lineLength) {
- final CharSequence sequence = text.subSequence(lastBreak, i);
- final int trimmedLength = TextUtils.getTrimmedLength(sequence);
- breakText.append(sequence, 0, trimmedLength);
- breakText.append('\n');
- lastBreak = i;
- runLength = 0;
- }
-
- runLength += textWidths[i];
-
- if (runLength > maxRunLength) {
- maxRunLength = (int) Math.ceil(runLength);
- }
- }
- breakText.append(text.subSequence(lastBreak, textLength));
-
+ // TODO: Implement minimum-difference line wrapping. Adding the results
+ // of Paint.getTextWidths() seems to return different values than
+ // StaticLayout.getWidth(), so this is non-trivial.
mHasMeasurements = true;
mLastMeasuredWidth = maxWidth;
-
- mLayout = new StaticLayout(breakText, paint, maxRunLength, Alignment.ALIGN_LEFT,
- mSpacingMult, mSpacingAdd, true);
+ mLayout = new StaticLayout(
+ mText, mTextPaint, maxWidth, mAlignment, mSpacingMult, mSpacingAdd, true);
return true;
}
@@ -316,54 +279,50 @@
final int innerPaddingX = mInnerPaddingX;
c.translate(mPaddingLeft + innerPaddingX, mPaddingTop);
- final RectF bounds = mLineBounds;
final int lineCount = layout.getLineCount();
- final Paint paint = layout.getPaint();
- paint.setShadowLayer(0, 0, 0, 0);
+ final Paint textPaint = mTextPaint;
+ final Paint paint = mPaint;
+ final RectF bounds = mLineBounds;
- final int backgroundColor = mBackgroundColor;
- if (Color.alpha(backgroundColor) > 0) {
- paint.setColor(backgroundColor);
- paint.setStyle(Style.FILL);
-
+ if (Color.alpha(mBackgroundColor) > 0) {
final float cornerRadius = mCornerRadius;
float previousBottom = layout.getLineTop(0);
+ paint.setColor(mBackgroundColor);
+ paint.setStyle(Style.FILL);
+
for (int i = 0; i < lineCount; i++) {
- bounds.left = layout.getLineLeft(i) - innerPaddingX;
+ bounds.left = layout.getLineLeft(i) -innerPaddingX;
bounds.right = layout.getLineRight(i) + innerPaddingX;
bounds.top = previousBottom;
bounds.bottom = layout.getLineBottom(i);
-
previousBottom = bounds.bottom;
c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
}
}
- final int edgeType = mEdgeType;
- if (edgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
- paint.setColor(mEdgeColor);
- paint.setStyle(Style.FILL_AND_STROKE);
- paint.setStrokeJoin(Join.ROUND);
- paint.setStrokeWidth(mOutlineWidth);
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
+ textPaint.setStrokeJoin(Join.ROUND);
+ textPaint.setStrokeWidth(mOutlineWidth);
+ textPaint.setColor(mEdgeColor);
+ textPaint.setStyle(Style.FILL_AND_STROKE);
for (int i = 0; i < lineCount; i++) {
layout.drawText(c, i, i);
}
+ } else if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ textPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor);
}
- if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
- paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor);
- }
-
- paint.setColor(mForegroundColor);
- paint.setStyle(Style.FILL);
+ textPaint.setColor(mForegroundColor);
+ textPaint.setStyle(Style.FILL);
for (int i = 0; i < lineCount; i++) {
layout.drawText(c, i, i);
}
+ textPaint.setShadowLayer(0, 0, 0, 0);
c.restoreToCount(saveCount);
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c15e18..b41b5b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1403,6 +1403,27 @@
android:label="@string/permlab_expandStatusBar"
android:description="@string/permdesc_expandStatusBar" />
+ <!-- ============================================================== -->
+ <!-- Permissions related to adding/removing shortcuts from Launcher -->
+ <!-- ============================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to install a shortcut in Launcher -->
+ <permission
+ android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_install_shortcut"
+ android:description="@string/permdesc_install_shortcut" />
+
+ <!-- Allows an application to uninstall a shortcut in Launcher -->
+ <permission
+ android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_uninstall_shortcut"
+ android:description="@string/permdesc_uninstall_shortcut"/>
+
<!-- ==================================================== -->
<!-- Permissions related to accessing sync settings -->
<!-- ==================================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 84a0d37..b3cb2a1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1255,4 +1255,8 @@
<!-- Threshold (in ms) under which a screen off / screen on will be considered a reset of the
transient navigation confirmation prompt.-->
<integer name="config_transient_navigation_confirmation_panic">5000</integer>
+
+ <!-- For some operators, PDU has garbages. To fix it, need to use valid index -->
+ <integer name="config_valid_wappush_index">-1</integer>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c57873e..c20c427 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -599,6 +599,22 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_expandStatusBar">Allows the app to expand or collapse the status bar.</string>
+ <!-- Title of an application permission, listed so the user can install application shortcuts
+ in their Launcher -->
+ <string name="permlab_install_shortcut">install shortcuts</string>
+ <!-- Description of an application permission, listed so the user can install application shortcuts
+ in their Launcher -->
+ <string name="permdesc_install_shortcut">Allows an application to add
+ Homescreen shortcuts without user intervention.</string>
+
+ <!-- Title of an application permission, listed so the user can uninstall application shortcuts
+ in their Launcher -->
+ <string name="permlab_uninstall_shortcut">uninstall shortcuts</string>
+ <!-- Description of an application permission, listed so the user can install application shortcuts
+ in their Launcher -->
+ <string name="permdesc_uninstall_shortcut">Allows the application to remove
+ Homescreen shortcuts without user intervention.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_processOutgoingCalls">reroute outgoing calls</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 14b319f..57a4bb7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -316,6 +316,7 @@
<java-symbol type="integer" name="config_safe_media_volume_index" />
<java-symbol type="integer" name="config_mobile_mtu" />
<java-symbol type="integer" name="config_volte_replacement_rat"/>
+ <java-symbol type="integer" name="config_valid_wappush_index" />
<java-symbol type="color" name="tab_indicator_text_v4" />
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c8ace44..a7c5b20 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -553,6 +553,11 @@
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
+ *
+ * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+ * if {@link InputStream#markSupported is.markSupported()} returns true,
+ * <code>is.mark(1024)</code> would be called. As of
+ * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
// we don't throw in this case, thus allowing the caller to only check
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 3524b25..3a99977 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -104,6 +104,11 @@
* allowing sharing may degrade the decoding speed.
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
* @throws IOException if the image format is not supported or can not be decoded.
+ *
+ * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
+ * if {@link InputStream#markSupported is.markSupported()} returns true,
+ * <code>is.mark(1024)</code> would be called. As of
+ * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
public static BitmapRegionDecoder newInstance(InputStream is,
boolean isShareable) throws IOException {
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 5e574a6..25d4c5e 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -41,7 +41,7 @@
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
-
+ inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index a63cac6..ed0a79a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -240,20 +240,20 @@
switch (bitmap->getConfig()) {
case SkBitmap::kA8_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_BYTE, bitmap->getPixels());
+ uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case SkBitmap::kRGB_565_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+ uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case SkBitmap::kARGB_8888_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_BYTE, bitmap->getPixels());
+ uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
// decoding happened
texture->blend = !bitmap->isOpaque();
@@ -293,17 +293,28 @@
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
+ uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
}
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
- GLenum type, const GLvoid * data) {
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
+ // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+ const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
+
if (resize) {
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
}
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
}
}; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 80bb22e..57fc19a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -125,8 +125,8 @@
void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
- GLenum type, const GLvoid * data);
+ void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index cbed3e4..d5f38b5 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -119,7 +119,7 @@
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
// With OpenGL ES 2.0 we have to upload entire stripes instead.
- mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
+ mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
}
CacheTexture::~CacheTexture() {
@@ -206,21 +206,21 @@
bool CacheTexture::upload() {
const Rect& dirtyRect = mDirtyRect;
- uint32_t x = mHasES3 ? dirtyRect.left : 0;
+ uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0;
uint32_t y = dirtyRect.top;
- uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
+ uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth;
uint32_t height = dirtyRect.getHeight();
// The unpack row length only needs to be specified when a new
// texture is bound
- if (mHasES3) {
+ if (mHasUnpackRowLength) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
}
mTexture->upload(x, y, width, height);
setDirty(false);
- return mHasES3;
+ return mHasUnpackRowLength;
}
void CacheTexture::setDirty(bool dirty) {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 028b611..61b38f8 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -190,7 +190,7 @@
uint32_t mMaxQuadCount;
Caches& mCaches;
CacheBlock* mCacheBlocks;
- bool mHasES3;
+ bool mHasUnpackRowLength;
Rect mDirtyRect;
};
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index edde68d..4dec081 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -17,6 +17,7 @@
package android.media;
import android.content.Context;
+import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.util.ArrayMap;
import android.util.AttributeSet;
@@ -1583,6 +1584,7 @@
}
final CueLayout cueBox = new CueLayout(getContext(), cue, mCaptionStyle, mFontSize);
+ mRegionCueBoxes.add(cueBox);
addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (getChildCount() > mRegion.mLines) {
@@ -1696,12 +1698,27 @@
removeAllViews();
+ final int cueAlignment = resolveCueAlignment(getLayoutDirection(), mCue.mAlignment);
+ final Alignment alignment;
+ switch (cueAlignment) {
+ case TextTrackCue.ALIGNMENT_LEFT:
+ alignment = Alignment.ALIGN_LEFT;
+ break;
+ case TextTrackCue.ALIGNMENT_RIGHT:
+ alignment = Alignment.ALIGN_RIGHT;
+ break;
+ case TextTrackCue.ALIGNMENT_MIDDLE:
+ default:
+ alignment = Alignment.ALIGN_CENTER;
+ }
+
final CaptionStyle captionStyle = mCaptionStyle;
final float fontSize = mFontSize;
final TextTrackCueSpan[][] lines = mCue.mLines;
final int lineCount = lines.length;
for (int i = 0; i < lineCount; i++) {
final SpanLayout lineBox = new SpanLayout(getContext(), lines[i]);
+ lineBox.setAlignment(alignment);
lineBox.setCaptionStyle(captionStyle, fontSize);
addView(lineBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 5cf05f8..ba6685a 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -149,6 +149,14 @@
<!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
<string name="keyguard_accessibility_transport_stop_description">Stop button</string>
+ <!-- Accessibility description for when the device prompts the user to dismiss keyguard
+ in order to complete an action. This will be followed by a message about the current
+ security option (e.g. "Pattern unlock."). [CHAR LIMIT=NONE] -->
+ <string name="keyguard_accessibility_show_bouncer">Unlock to continue</string>
+
+ <!-- Accessibility description for when the bouncer prompt is dismissed. [CHAR LIMIT=NONE] -->
+ <string name="keyguard_accessibility_hide_bouncer">Launch canceled</string>
+
<!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="password_keyboard_label_symbol_key">\?123</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index aa43711..a9e9d3a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -24,7 +24,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AlertDialog;
-import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetHost;
@@ -41,7 +40,6 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.media.RemoteControlClient;
-import android.os.Bundle;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
@@ -49,11 +47,9 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -206,6 +202,13 @@
}
}
+ public void announceCurrentSecurityMethod() {
+ View v = (View) getSecurityView(mCurrentSecuritySelection);
+ if (v != null) {
+ v.announceForAccessibility(v.getContentDescription());
+ }
+ }
+
private void getInitialTransportState() {
DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
.getCachedDisplayClientState();
@@ -1663,4 +1666,8 @@
mAppWidgetContainer.handleExternalCameraEvent(event);
}
+ public void launchCamera() {
+ mActivityLauncher.launchCamera(getHandler(), null);
+ }
+
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index 77006c5..d7c5fe2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -137,6 +137,10 @@
checkPermission();
mKeyguardViewMediator.dispatch(event);
}
+ public void launchCamera() {
+ checkPermission();
+ mKeyguardViewMediator.launchCamera();
+ }
};
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index 177e0f8..a0e44d7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -506,4 +506,10 @@
mKeyguardView.dispatch(event);
}
}
+
+ public void launchCamera() {
+ if (mKeyguardView != null) {
+ mKeyguardView.launchCamera();
+ }
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 478096c..0606d83 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -122,6 +122,7 @@
private static final int KEYGUARD_TIMEOUT = 13;
private static final int SHOW_ASSISTANT = 14;
private static final int DISPATCH_EVENT = 15;
+ private static final int LAUNCH_CAMERA = 16;
/**
* The default amount of time we stay awake (used for all key input)
@@ -1071,6 +1072,9 @@
case DISPATCH_EVENT:
handleDispatchEvent((MotionEvent) msg.obj);
break;
+ case LAUNCH_CAMERA:
+ handleLaunchCamera();
+ break;
}
}
};
@@ -1107,6 +1111,10 @@
sendUserPresentBroadcast();
}
+ protected void handleLaunchCamera() {
+ mKeyguardViewManager.launchCamera();
+ }
+
protected void handleDispatchEvent(MotionEvent event) {
mKeyguardViewManager.dispatch(event);
}
@@ -1341,4 +1349,9 @@
Message msg = mHandler.obtainMessage(DISPATCH_EVENT, event);
mHandler.sendMessage(msg);
}
+
+ public void launchCamera() {
+ Message msg = mHandler.obtainMessage(LAUNCH_CAMERA);
+ mHandler.sendMessage(msg);
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
index d9f9471..d1862cd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -87,6 +87,11 @@
}
public void showBouncer(boolean show) {
+ CharSequence what = mKeyguardHostView.getContext().getResources().getText(
+ show ? R.string.keyguard_accessibility_show_bouncer
+ : R.string.keyguard_accessibility_hide_bouncer);
+ mKeyguardHostView.announceForAccessibility(what);
+ mKeyguardHostView.announceCurrentSecurityMethod();
mChallengeLayout.showBouncer();
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index f7dc058..f8857ab 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -90,6 +90,7 @@
// Background worker thread: used here for persistence, also made available to widget frames
private final HandlerThread mBackgroundWorkerThread;
private final Handler mBackgroundWorkerHandler;
+ private boolean mCameraEventInProgress;
public KeyguardWidgetPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -941,14 +942,18 @@
beginCameraEvent();
int cameraPage = getPageCount() - 1;
boolean endWarp = false;
- if (isCameraPage(cameraPage)) {
+ if (isCameraPage(cameraPage) || mCameraEventInProgress) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ // Once we start dispatching camera events, we must continue to do so
+ // to keep event dispatch happy.
+ mCameraEventInProgress = true;
userActivity();
startWarp(cameraPage);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mCameraEventInProgress = false;
endWarp = true;
break;
}
diff --git a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
index 765b274..aa7256b 100644
--- a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
@@ -153,6 +153,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<com.android.systemui.statusbar.policy.DeadZone
@@ -297,6 +298,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<com.android.systemui.statusbar.policy.DeadZone
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 9592b18..b9ad799 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -149,6 +149,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<com.android.systemui.statusbar.policy.DeadZone
@@ -290,6 +291,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<com.android.systemui.statusbar.policy.DeadZone
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 11cbbc7..aa365ae 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -157,6 +157,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
@@ -167,6 +168,7 @@
android:src="@drawable/ic_sysbar_camera"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_camera_button"
/>
</FrameLayout>
@@ -312,6 +314,7 @@
android:src="@drawable/search_light"
android:scaleType="center"
android:visibility="gone"
+ android:contentDescription="@string/accessibility_search_light"
/>
<!-- No camera button in landscape mode -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index eb425e6..58865ab 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -192,6 +192,10 @@
<string name="accessibility_menu">Menu</string>
<!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_recent">Recent apps</string>
+ <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_search_light">Search</string>
+ <!-- Content description of the camera button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_camera_button">Camera</string>
<!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ime_switch_button">Switch input method button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 3a5524d..3a82753 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -50,8 +50,7 @@
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mSourceView == null || mDelegateView == null || mDisabled
- || mBar.shouldDisableNavbarGestures()) {
+ if (mSourceView == null || mDelegateView == null || mBar.shouldDisableNavbarGestures()) {
return false;
}
@@ -73,7 +72,7 @@
return false;
}
- if (!mPanelShowing && action == MotionEvent.ACTION_MOVE) {
+ if (!mDisabled && !mPanelShowing && action == MotionEvent.ACTION_MOVE) {
final int historySize = event.getHistorySize();
for (int k = 0; k < historySize + 1; k++) {
float x = k < historySize ? event.getHistoricalX(k) : event.getX();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index a6e2347..1221a55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -103,4 +103,30 @@
return false;
}
+ public void showAssistant() {
+ if (mService != null) {
+ try {
+ mService.showAssistant();
+ } catch (RemoteException e) {
+ // What to do?
+ Log.e(TAG, "RemoteException launching assistant!", e);
+ }
+ } else {
+ Log.w(TAG, "dispatch(event): NO SERVICE!");
+ }
+ }
+
+ public void launchCamera() {
+ if (mService != null) {
+ try {
+ mService.launchCamera();
+ } catch (RemoteException e) {
+ // What to do?
+ Log.e(TAG, "RemoteException launching camera!", e);
+ }
+ } else {
+ Log.w(TAG, "dispatch(event): NO SERVICE!");
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 24e27b1..596fac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -37,8 +37,10 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -86,7 +88,7 @@
// used to disable the camera icon in navbar when disabled by DPM
private boolean mCameraDisabledByDpm;
- KeyguardTouchDelegate mTouchDelegate;
+ KeyguardTouchDelegate mKeyguardTouchDelegate;
private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
@Override
@@ -110,7 +112,7 @@
}
break;
}
- return mTouchDelegate.dispatch(event);
+ return mKeyguardTouchDelegate.dispatch(event);
}
};
@@ -153,7 +155,7 @@
mBarTransitions = new NavigationBarTransitions(this);
- mTouchDelegate = new KeyguardTouchDelegate(mContext);
+ mKeyguardTouchDelegate = new KeyguardTouchDelegate(mContext);
mCameraDisabledByDpm = isCameraDisabledByDpm();
watchForDevicePolicyChanges();
@@ -339,7 +341,7 @@
final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
final boolean disabledBecauseKeyguardSecure =
(disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
- && mTouchDelegate.isSecure();
+ && mKeyguardTouchDelegate.isSecure();
return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
} catch (RemoteException e) {
Log.e(TAG, "Can't get userId", e);
@@ -389,12 +391,44 @@
mCurrentView = mRotatedViews[Surface.ROTATION_0];
- // Add a touch handler for camera icon for all view orientations.
- for (int i = 0; i < mRotatedViews.length; i++) {
- View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
- if (cameraButton != null) {
- cameraButton.setOnTouchListener(mCameraTouchListener);
+
+ final AccessibilityManager accessibilityManager =
+ (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityManager.isEnabled()) {
+ // In accessibility mode, we add a simple click handler since swipe is tough to
+ // trigger near screen edges.
+ View camera = getCameraButton();
+ View searchLight = getSearchLight();
+ if (camera != null || searchLight != null) {
+ OnClickListener listener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ launchForAccessibilityClick(v);
+ }
+ };
+ if (camera != null) {
+ camera.setOnClickListener(listener);
+ }
+ if (searchLight != null) {
+ searchLight.setOnClickListener(listener);
+ }
}
+ } else {
+ // Add a touch handler for camera icon for all view orientations.
+ for (int i = 0; i < mRotatedViews.length; i++) {
+ View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
+ if (cameraButton != null) {
+ cameraButton.setOnTouchListener(mCameraTouchListener);
+ }
+ }
+ }
+ }
+
+ protected void launchForAccessibilityClick(View v) {
+ if (v == getCameraButton()) {
+ mKeyguardTouchDelegate.launchCamera();
+ } else if (v == getSearchLight()) {
+ mKeyguardTouchDelegate.showAssistant();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index b27584d..5e299ee 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -192,6 +192,10 @@
// Not used by PhoneWindowManager. See code in {@link NavigationBarView}
}
+ public void launchCamera() {
+ // Not used by PhoneWindowManager. See code in {@link NavigationBarView}
+ }
+
@Override
public IBinder asBinder() {
return mService.asBinder();