Merge "remove toast from animation benchmark"
diff --git a/Android.mk b/Android.mk
index 5879bb5..f65f9e4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -117,6 +117,7 @@
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \
core/java/android/net/INetworkStatsService.aidl \
+ core/java/android/net/nsd/INsdManager.aidl \
core/java/android/nfc/INdefPushCallback.aidl \
core/java/android/nfc/INfcAdapter.aidl \
core/java/android/nfc/INfcAdapterExtras.aidl \
diff --git a/NOTICE b/NOTICE
index 152be20..4e69963 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,6 +56,17 @@
=========================================================================
== NOTICE file corresponding to the section 4 d of ==
== the Apache License, Version 2.0, ==
+ == in this case for the mDnsResponder code. ==
+ =========================================================================
+
+mDnsResponder TXTRecord
+This file is Copyright 2004 Apple Computer, Inc. but released under
+the Apache2 License.
+
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
== in this case for the TagSoup code. ==
=========================================================================
diff --git a/api/current.txt b/api/current.txt
index 95007b5..9186952 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -596,7 +596,7 @@
field public static final int layerType = 16843604; // 0x1010354
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
- field public static final int layoutDirection = 16843690; // 0x10103aa
+ field public static final int layoutDirection = 16843691; // 0x10103ab
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -618,10 +618,10 @@
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
field public static final int layout_marginBottom = 16843002; // 0x10100fa
- field public static final int layout_marginEnd = 16843694; // 0x10103ae
+ field public static final int layout_marginEnd = 16843695; // 0x10103af
field public static final int layout_marginLeft = 16842999; // 0x10100f7
field public static final int layout_marginRight = 16843001; // 0x10100f9
- field public static final int layout_marginStart = 16843693; // 0x10103ad
+ field public static final int layout_marginStart = 16843694; // 0x10103ae
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -717,10 +717,10 @@
field public static final int packageNames = 16843649; // 0x1010381
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
- field public static final int paddingEnd = 16843692; // 0x10103ac
+ field public static final int paddingEnd = 16843693; // 0x10103ad
field public static final int paddingLeft = 16842966; // 0x10100d6
field public static final int paddingRight = 16842968; // 0x10100d8
- field public static final int paddingStart = 16843691; // 0x10103ab
+ field public static final int paddingStart = 16843692; // 0x10103ac
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -962,6 +962,7 @@
field public static final int tension = 16843370; // 0x101026a
field public static final int testOnly = 16843378; // 0x1010272
field public static final int text = 16843087; // 0x101014f
+ field public static final int textAlignment = 16843690; // 0x10103aa
field public static final int textAllCaps = 16843660; // 0x101038c
field public static final int textAppearance = 16842804; // 0x1010034
field public static final int textAppearanceButton = 16843271; // 0x1010207
@@ -12899,6 +12900,7 @@
method public byte[] getHistoricalBytes();
method public int getMaxTransceiveLength();
method public int getTimeout();
+ method public boolean isExtendedLengthApduSupported();
method public void setTimeout(int);
method public byte[] transceive(byte[]) throws java.io.IOException;
}
@@ -23209,6 +23211,7 @@
method public void buildLayer();
method public boolean callOnClick();
method public boolean canResolveLayoutDirection();
+ method public boolean canResolveTextAlignment();
method public boolean canResolveTextDirection();
method public boolean canScrollHorizontally(int);
method public boolean canScrollVertically(int);
@@ -23311,6 +23314,8 @@
method public final int getMeasuredState();
method public final int getMeasuredWidth();
method public final int getMeasuredWidthAndState();
+ method public int getMinimumHeight();
+ method public int getMinimumWidth();
method public int getNextFocusDownId();
method public int getNextFocusForwardId();
method public int getNextFocusLeftId();
@@ -23329,6 +23334,7 @@
method public float getPivotY();
method public int getResolvedLayoutDirection();
method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
+ method public int getResolvedTextAlignment();
method public int getResolvedTextDirection();
method public android.content.res.Resources getResources();
method public final int getRight();
@@ -23340,6 +23346,9 @@
method public float getRotationY();
method public float getScaleX();
method public float getScaleY();
+ method public int getScrollBarDefaultDelayBeforeFade();
+ method public int getScrollBarFadeDuration();
+ method public int getScrollBarSize();
method public int getScrollBarStyle();
method public final int getScrollX();
method public final int getScrollY();
@@ -23349,6 +23358,7 @@
method public int getSystemUiVisibility();
method public java.lang.Object getTag();
method public java.lang.Object getTag(int);
+ method public int getTextAlignment();
method public int getTextDirection();
method public final int getTop();
method protected float getTopFadingEdgeStrength();
@@ -23409,6 +23419,7 @@
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
+ method public boolean isScrollContainer();
method public boolean isScrollbarFadingEnabled();
method public boolean isSelected();
method public boolean isShown();
@@ -23456,6 +23467,8 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onResolvedLayoutDirectionChanged();
method public void onResolvedLayoutDirectionReset();
+ method public void onResolvedTextAlignmentChanged();
+ method public void onResolvedTextAlignmentReset();
method public void onResolvedTextDirectionChanged();
method public void onResolvedTextDirectionReset();
method protected void onRestoreInstanceState(android.os.Parcelable);
@@ -23496,11 +23509,13 @@
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public void resetResolvedLayoutDirection();
+ method public void resetResolvedTextAlignment();
method public void resetResolvedTextDirection();
method public void resolveLayoutDirection();
method public void resolvePadding();
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
+ method public void resolveTextAlignment();
method public void resolveTextDirection();
method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -23513,8 +23528,9 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
- method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+ method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundResource(int);
method public final void setBottom(int);
method public void setCameraDistance(float);
@@ -23574,6 +23590,9 @@
method public void setSaveFromParentEnabled(boolean);
method public void setScaleX(float);
method public void setScaleY(float);
+ method public void setScrollBarDefaultDelayBeforeFade(int);
+ method public void setScrollBarFadeDuration(int);
+ method public void setScrollBarSize(int);
method public void setScrollBarStyle(int);
method public void setScrollContainer(boolean);
method public void setScrollX(int);
@@ -23584,6 +23603,7 @@
method public void setSystemUiVisibility(int);
method public void setTag(java.lang.Object);
method public void setTag(int, java.lang.Object);
+ method public void setTextAlignment(int);
method public void setTextDirection(int);
method public final void setTop(int);
method public void setTouchDelegate(android.view.TouchDelegate);
@@ -23694,6 +23714,15 @@
field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
+ field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
+ field protected static int TEXT_ALIGNMENT_DEFAULT;
+ field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
+ field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
+ field public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = 131072; // 0x20000
+ field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
+ field public static final int TEXT_ALIGNMENT_TEXT_START = 2; // 0x2
+ field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
+ field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
field protected static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d758ecae..5ffceb3 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -59,6 +59,8 @@
import android.net.ThrottleManager;
import android.net.IThrottleManager;
import android.net.Uri;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.IWifiP2pManager;
@@ -372,6 +374,14 @@
ctx.mMainThread.getHandler());
}});
+ registerService(NSD_SERVICE, new ServiceFetcher() {
+ @Override
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(NSD_SERVICE);
+ INsdManager service = INsdManager.Stub.asInterface(b);
+ return new NsdManager(service);
+ }});
+
// Note: this was previously cached in a static variable, but
// constructed using mMainThread.getHandler(), so converting
// it to be a regular Context-cached service...
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2902504..98ed117 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1769,6 +1769,18 @@
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.NsdManager} for handling management of network service
+ * discovery
+ *
+ * @hide
+ * @see #getSystemService
+ * @see android.net.NsdManager
+ */
+ public static final String NSD_SERVICE = "servicediscovery";
+
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
* ringer modes and audio routing.
diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/DnsSdServiceInfo.java
new file mode 100644
index 0000000..47d6ec6
--- /dev/null
+++ b/core/java/android/net/nsd/DnsSdServiceInfo.java
@@ -0,0 +1,136 @@
+/*
+ * 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.net.nsd;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Defines a service based on DNS service discovery
+ * {@hide}
+ */
+public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
+
+ private String mServiceName;
+
+ private String mRegistrationType;
+
+ private DnsSdTxtRecord mTxtRecord;
+
+ private String mHostname;
+
+ private int mPort;
+
+ DnsSdServiceInfo() {
+ }
+
+ DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
+ mServiceName = sn;
+ mRegistrationType = rt;
+ mTxtRecord = tr;
+ }
+
+ @Override
+ /** @hide */
+ public String getServiceName() {
+ return mServiceName;
+ }
+
+ @Override
+ /** @hide */
+ public void setServiceName(String s) {
+ mServiceName = s;
+ }
+
+ @Override
+ /** @hide */
+ public String getServiceType() {
+ return mRegistrationType;
+ }
+
+ @Override
+ /** @hide */
+ public void setServiceType(String s) {
+ mRegistrationType = s;
+ }
+
+ public DnsSdTxtRecord getTxtRecord() {
+ return mTxtRecord;
+ }
+
+ public void setTxtRecord(DnsSdTxtRecord t) {
+ mTxtRecord = new DnsSdTxtRecord(t);
+ }
+
+ public String getHostName() {
+ return mHostname;
+ }
+
+ public void setHostName(String s) {
+ mHostname = s;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public void setPort(int p) {
+ mPort = p;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("name: ").append(mServiceName).
+ append("type: ").append(mRegistrationType).
+ append("txtRecord: ").append(mTxtRecord);
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mServiceName);
+ dest.writeString(mRegistrationType);
+ dest.writeParcelable(mTxtRecord, flags);
+ dest.writeString(mHostname);
+ dest.writeInt(mPort);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<DnsSdServiceInfo> CREATOR =
+ new Creator<DnsSdServiceInfo>() {
+ public DnsSdServiceInfo createFromParcel(Parcel in) {
+ DnsSdServiceInfo info = new DnsSdServiceInfo();
+ info.mServiceName = in.readString();
+ info.mRegistrationType = in.readString();
+ info.mTxtRecord = in.readParcelable(null);
+ info.mHostname = in.readString();
+ info.mPort = in.readInt();
+ return info;
+ }
+
+ public DnsSdServiceInfo[] newArray(int size) {
+ return new DnsSdServiceInfo[size];
+ }
+ };
+
+}
diff --git a/core/java/android/net/nsd/DnsSdTxtRecord.java b/core/java/android/net/nsd/DnsSdTxtRecord.java
new file mode 100644
index 0000000..6d4342c
--- /dev/null
+++ b/core/java/android/net/nsd/DnsSdTxtRecord.java
@@ -0,0 +1,305 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To do:
+ - implement remove()
+ - fix set() to replace existing values
+ */
+
+package android.net.nsd;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * This class handles TXT record data for DNS based service discovery as specified at
+ * http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11
+ *
+ * DNS-SD specifies that a TXT record corresponding to an SRV record consist of
+ * a packed array of bytes, each preceded by a length byte. Each string
+ * is an attribute-value pair.
+ *
+ * The DnsSdTxtRecord object stores the entire TXT data as a single byte array, traversing it
+ * as need be to implement its various methods.
+ *
+ * @hide
+ */
+public class DnsSdTxtRecord implements Parcelable {
+ private static final byte mSeperator = '=';
+
+ private byte[] mData;
+
+ /** Constructs a new, empty TXT record. */
+ public DnsSdTxtRecord() {
+ mData = new byte[0];
+ }
+
+ /** Constructs a new TXT record from a byte array in the standard format. */
+ public DnsSdTxtRecord(byte[] data) {
+ mData = (byte[]) data.clone();
+ }
+
+ /** Copy constructor */
+ public DnsSdTxtRecord(DnsSdTxtRecord src) {
+ if (src != null && src.mData != null) {
+ mData = (byte[]) src.mData.clone();
+ }
+ }
+
+ /**
+ * Set a key/value pair. Setting an existing key will replace its value.
+ * @param key Must be ascii with no '='
+ * @param value matching value to key
+ */
+ public void set(String key, String value) {
+ byte[] keyBytes;
+ byte[] valBytes;
+ int valLen;
+
+ if (value != null) {
+ valBytes = value.getBytes();
+ valLen = valBytes.length;
+ } else {
+ valBytes = null;
+ valLen = 0;
+ }
+
+ try {
+ keyBytes = key.getBytes("US-ASCII");
+ }
+ catch (java.io.UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("key should be US-ASCII");
+ }
+
+ for (int i = 0; i < keyBytes.length; i++) {
+ if (keyBytes[i] == '=') {
+ throw new IllegalArgumentException("= is not a valid character in key");
+ }
+ }
+
+ if (keyBytes.length + valLen >= 255) {
+ throw new IllegalArgumentException("Key and Value length cannot exceed 255 bytes");
+ }
+
+ int currentLoc = remove(key);
+ if (currentLoc == -1)
+ currentLoc = keyCount();
+
+ insert(keyBytes, valBytes, currentLoc);
+ }
+
+ /**
+ * Get a value for a key
+ *
+ * @param key
+ * @return The value associated with the key
+ */
+ public String get(String key) {
+ byte[] val = this.getValue(key);
+ return val != null ? new String(val) : null;
+ }
+
+ /** Remove a key/value pair. If found, returns the index or -1 if not found */
+ public int remove(String key) {
+ int avStart = 0;
+
+ for (int i=0; avStart < mData.length; i++) {
+ int avLen = mData[avStart];
+ if (key.length() <= avLen &&
+ (key.length() == avLen || mData[avStart + key.length() + 1] == mSeperator)) {
+ String s = new String(mData, avStart + 1, key.length());
+ if (0 == key.compareToIgnoreCase(s)) {
+ byte[] oldBytes = mData;
+ mData = new byte[oldBytes.length - avLen - 1];
+ System.arraycopy(oldBytes, 0, mData, 0, avStart);
+ System.arraycopy(oldBytes, avStart + avLen + 1, mData, avStart,
+ oldBytes.length - avStart - avLen - 1);
+ return i;
+ }
+ }
+ avStart += (0xFF & (avLen + 1));
+ }
+ return -1;
+ }
+
+ /** Return the count of keys */
+ public int keyCount() {
+ int count = 0, nextKey;
+ for (nextKey = 0; nextKey < mData.length; count++) {
+ nextKey += (0xFF & (mData[nextKey] + 1));
+ }
+ return count;
+ }
+
+ /** Return true if key is present, false if not. */
+ public boolean contains(String key) {
+ String s = null;
+ for (int i = 0; null != (s = this.getKey(i)); i++) {
+ if (0 == key.compareToIgnoreCase(s)) return true;
+ }
+ return false;
+ }
+
+ /* Gets the size in bytes */
+ public int size() {
+ return mData.length;
+ }
+
+ /* Gets the raw data in bytes */
+ public byte[] getRawData() {
+ return mData;
+ }
+
+ private void insert(byte[] keyBytes, byte[] value, int index) {
+ byte[] oldBytes = mData;
+ int valLen = (value != null) ? value.length : 0;
+ int insertion = 0;
+ int newLen, avLen;
+
+ for (int i = 0; i < index && insertion < mData.length; i++) {
+ insertion += (0xFF & (mData[insertion] + 1));
+ }
+
+ avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
+ newLen = avLen + oldBytes.length + 1;
+
+ mData = new byte[newLen];
+ System.arraycopy(oldBytes, 0, mData, 0, insertion);
+ int secondHalfLen = oldBytes.length - insertion;
+ System.arraycopy(oldBytes, insertion, mData, newLen - secondHalfLen, secondHalfLen);
+ mData[insertion] = (byte) avLen;
+ System.arraycopy(keyBytes, 0, mData, insertion + 1, keyBytes.length);
+ if (value != null) {
+ mData[insertion + 1 + keyBytes.length] = mSeperator;
+ System.arraycopy(value, 0, mData, insertion + keyBytes.length + 2, valLen);
+ }
+ }
+
+ /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
+ private String getKey(int index) {
+ int avStart = 0;
+
+ for (int i=0; i < index && avStart < mData.length; i++) {
+ avStart += mData[avStart] + 1;
+ }
+
+ if (avStart < mData.length) {
+ int avLen = mData[avStart];
+ int aLen = 0;
+
+ for (aLen=0; aLen < avLen; aLen++) {
+ if (mData[avStart + aLen + 1] == mSeperator) break;
+ }
+ return new String(mData, avStart + 1, aLen);
+ }
+ return null;
+ }
+
+ /**
+ * Look up a key in the TXT record by zero-based index and return its value.
+ * Returns null if index exceeds the total number of keys.
+ * Returns null if the key is present with no value.
+ */
+ private byte[] getValue(int index) {
+ int avStart = 0;
+ byte[] value = null;
+
+ for (int i=0; i < index && avStart < mData.length; i++) {
+ avStart += mData[avStart] + 1;
+ }
+
+ if (avStart < mData.length) {
+ int avLen = mData[avStart];
+ int aLen = 0;
+
+ for (aLen=0; aLen < avLen; aLen++) {
+ if (mData[avStart + aLen + 1] == mSeperator) {
+ value = new byte[avLen - aLen - 1];
+ System.arraycopy(mData, avStart + aLen + 2, value, 0, avLen - aLen - 1);
+ break;
+ }
+ }
+ }
+ return value;
+ }
+
+ private String getValueAsString(int index) {
+ byte[] value = this.getValue(index);
+ return value != null ? new String(value) : null;
+ }
+
+ private byte[] getValue(String forKey) {
+ String s = null;
+ int i;
+
+ for (i = 0; null != (s = this.getKey(i)); i++) {
+ if (0 == forKey.compareToIgnoreCase(s)) {
+ return this.getValue(i);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return a string representation.
+ * Example : {key1=value1},{key2=value2}..
+ *
+ * For a key say like "key3" with null value
+ * {key1=value1},{key2=value2}{key3}
+ */
+ public String toString() {
+ String a, result = null;
+
+ for (int i = 0; null != (a = this.getKey(i)); i++) {
+ String av = "{" + a;
+ String val = this.getValueAsString(i);
+ if (val != null)
+ av += "=" + val + "}";
+ else
+ av += "}";
+ if (result == null)
+ result = av;
+ else
+ result = result + ", " + av;
+ }
+ return result != null ? result : "";
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mData);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<DnsSdTxtRecord> CREATOR =
+ new Creator<DnsSdTxtRecord>() {
+ public DnsSdTxtRecord createFromParcel(Parcel in) {
+ DnsSdTxtRecord info = new DnsSdTxtRecord();
+ in.readByteArray(info.mData);
+ return info;
+ }
+
+ public DnsSdTxtRecord[] newArray(int size) {
+ return new DnsSdTxtRecord[size];
+ }
+ };
+}
diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl
new file mode 100644
index 0000000..077a675
--- /dev/null
+++ b/core/java/android/net/nsd/INsdManager.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.net.nsd;
+
+import android.os.Messenger;
+
+/**
+ * Interface that NsdService implements
+ *
+ * {@hide}
+ */
+interface INsdManager
+{
+ Messenger getMessenger();
+}
diff --git a/core/java/android/net/nsd/NetworkServiceInfo.java b/core/java/android/net/nsd/NetworkServiceInfo.java
new file mode 100644
index 0000000..34d83d1
--- /dev/null
+++ b/core/java/android/net/nsd/NetworkServiceInfo.java
@@ -0,0 +1,32 @@
+/*
+ * 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.net.nsd;
+
+/**
+ * Interface for a network service.
+ *
+ * {@hide}
+ */
+public interface NetworkServiceInfo {
+
+ String getServiceName();
+ void setServiceName(String s);
+
+ String getServiceType();
+ void setServiceType(String s);
+
+}
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
new file mode 100644
index 0000000..a109a98
--- /dev/null
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -0,0 +1,394 @@
+/*
+ * 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.net.nsd;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+/**
+ * The Network Service Discovery Manager class provides the API for service
+ * discovery. Service discovery enables applications to discover and connect with services
+ * on a network. Example applications include a game application discovering another instance
+ * of the game application or a printer application discovering other printers on a network.
+ *
+ * <p> The API is asynchronous and responses to requests from an application are on listener
+ * callbacks provided by the application. The application needs to do an initialization with
+ * {@link #initialize} before doing any operation.
+ *
+ * <p> Android currently supports DNS based service discovery and it is limited to a local
+ * network with the use of multicast DNS. In future, this class will be
+ * extended to support other service discovery mechanisms.
+ *
+ * Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService(Context.NSD_SERVICE)}.
+ * @hide
+ *
+ */
+public class NsdManager {
+ private static final String TAG = "NsdManager";
+ INsdManager mService;
+
+ private static final int BASE = Protocol.BASE_NSD_MANAGER;
+
+ /** @hide */
+ public static final int DISCOVER_SERVICES = BASE + 1;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
+ /** @hide */
+ public static final int SERVICE_FOUND = BASE + 4;
+ /** @hide */
+ public static final int SERVICE_LOST = BASE + 5;
+
+ /** @hide */
+ public static final int STOP_DISCOVERY = BASE + 6;
+ /** @hide */
+ public static final int STOP_DISCOVERY_FAILED = BASE + 7;
+ /** @hide */
+ public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
+
+ /** @hide */
+ public static final int REGISTER_SERVICE = BASE + 9;
+ /** @hide */
+ public static final int REGISTER_SERVICE_FAILED = BASE + 10;
+ /** @hide */
+ public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
+
+ /** @hide */
+ public static final int UPDATE_SERVICE = BASE + 12;
+ /** @hide */
+ public static final int UPDATE_SERVICE_FAILED = BASE + 13;
+ /** @hide */
+ public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 14;
+
+ /** @hide */
+ public static final int RESOLVE_SERVICE = BASE + 15;
+ /** @hide */
+ public static final int RESOLVE_SERVICE_FAILED = BASE + 16;
+ /** @hide */
+ public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 17;
+
+ /**
+ * Create a new Nsd instance. Applications use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
+ * @param service the Binder interface
+ * @hide - hide this because it takes in a parameter of type INsdManager, which
+ * is a system private class.
+ */
+ public NsdManager(INsdManager service) {
+ mService = service;
+ }
+
+ /**
+ * Indicates that the operation failed due to an internal error.
+ */
+ public static final int ERROR = 0;
+
+ /**
+ * Indicates that the operation failed because service discovery is unsupported on the device.
+ */
+ public static final int UNSUPPORTED = 1;
+
+ /**
+ * Indicates that the operation failed because the framework is busy and
+ * unable to service the request
+ */
+ public static final int BUSY = 2;
+
+ /** Interface for callback invocation when framework channel is connected or lost */
+ public interface ChannelListener {
+ public void onChannelConnected(Channel c);
+ /**
+ * The channel to the framework has been disconnected.
+ * Application could try re-initializing using {@link #initialize}
+ */
+ public void onChannelDisconnected();
+ }
+
+ public interface ActionListener {
+
+ public void onFailure(int errorCode);
+
+ public void onSuccess();
+ }
+
+ public interface DnsSdDiscoveryListener {
+
+ public void onFailure(int errorCode);
+
+ public void onStarted(String registrationType);
+
+ public void onServiceFound(DnsSdServiceInfo serviceInfo);
+
+ public void onServiceLost(DnsSdServiceInfo serviceInfo);
+
+ }
+
+ public interface DnsSdRegisterListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo);
+ }
+
+ public interface DnsSdUpdateRegistrationListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
+ }
+
+ public interface DnsSdResolveListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceResolved(DnsSdServiceInfo serviceInfo);
+ }
+
+ /**
+ * A channel that connects the application to the NetworkService framework.
+ * Most service operations require a Channel as an argument. An instance of Channel is obtained
+ * by doing a call on {@link #initialize}
+ */
+ public static class Channel {
+ Channel(Looper looper, ChannelListener l) {
+ mAsyncChannel = new AsyncChannel();
+ mHandler = new ServiceHandler(looper);
+ mChannelListener = l;
+ }
+ private ChannelListener mChannelListener;
+ private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
+ private ActionListener mDnsSdStopDiscoveryListener;
+ private DnsSdRegisterListener mDnsSdRegisterListener;
+ private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
+ private DnsSdResolveListener mDnsSdResolveListener;
+
+ AsyncChannel mAsyncChannel;
+ ServiceHandler mHandler;
+ class ServiceHandler extends Handler {
+ ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ if (mChannelListener != null) {
+ mChannelListener.onChannelConnected(Channel.this);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (mChannelListener != null) {
+ mChannelListener.onChannelDisconnected();
+ mChannelListener = null;
+ }
+ break;
+ case DISCOVER_SERVICES_STARTED:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onStarted((String) message.obj);
+ }
+ break;
+ case DISCOVER_SERVICES_FAILED:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onFailure(message.arg1);
+ }
+ break;
+ case SERVICE_FOUND:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onServiceFound(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case SERVICE_LOST:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onServiceLost(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case STOP_DISCOVERY_FAILED:
+ if (mDnsSdStopDiscoveryListener != null) {
+ mDnsSdStopDiscoveryListener.onFailure(message.arg1);
+ }
+ break;
+ case STOP_DISCOVERY_SUCCEEDED:
+ if (mDnsSdStopDiscoveryListener != null) {
+ mDnsSdStopDiscoveryListener.onSuccess();
+ }
+ break;
+ case REGISTER_SERVICE_FAILED:
+ if (mDnsSdRegisterListener != null) {
+ mDnsSdRegisterListener.onFailure(message.arg1);
+ }
+ break;
+ case REGISTER_SERVICE_SUCCEEDED:
+ if (mDnsSdRegisterListener != null) {
+ mDnsSdRegisterListener.onServiceRegistered(message.arg1,
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case UPDATE_SERVICE_FAILED:
+ if (mDnsSdUpdateListener != null) {
+ mDnsSdUpdateListener.onFailure(message.arg1);
+ }
+ break;
+ case UPDATE_SERVICE_SUCCEEDED:
+ if (mDnsSdUpdateListener != null) {
+ mDnsSdUpdateListener.onServiceUpdated(message.arg1,
+ (DnsSdTxtRecord) message.obj);
+ }
+ break;
+ case RESOLVE_SERVICE_FAILED:
+ if (mDnsSdResolveListener != null) {
+ mDnsSdResolveListener.onFailure(message.arg1);
+ }
+ break;
+ case RESOLVE_SERVICE_SUCCEEDED:
+ if (mDnsSdResolveListener != null) {
+ mDnsSdResolveListener.onServiceResolved(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ default:
+ Log.d(TAG, "Ignored " + message);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers the application with the service discovery framework. This function
+ * must be the first to be called before any other operations are performed. No service
+ * discovery operations must be performed until the ChannelListener callback notifies
+ * that the channel is connected
+ *
+ * @param srcContext is the context of the source
+ * @param srcLooper is the Looper on which the callbacks are receivied
+ * @param listener for callback at loss of framework communication.
+ */
+ public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
+ Messenger messenger = getMessenger();
+ if (messenger == null) throw new RuntimeException("Failed to initialize");
+ if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null");
+
+ Channel c = new Channel(srcLooper, listener);
+ c.mAsyncChannel.connect(srcContext, c.mHandler, messenger);
+ }
+
+ /**
+ * Set the listener for service discovery. Can be null.
+ */
+ public void setDiscoveryListener(Channel c, DnsSdDiscoveryListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdDiscoveryListener = b;
+ }
+
+ /**
+ * Set the listener for stop service discovery. Can be null.
+ */
+ public void setStopDiscoveryListener(Channel c, ActionListener a) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdStopDiscoveryListener = a;
+ }
+
+ /**
+ * Set the listener for service registration. Can be null.
+ */
+ public void setRegisterListener(Channel c, DnsSdRegisterListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdRegisterListener = b;
+ }
+
+ /**
+ * Set the listener for service registration. Can be null.
+ */
+ public void setUpdateRegistrationListener(Channel c, DnsSdUpdateRegistrationListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdUpdateListener = b;
+ }
+
+ /**
+ * Set the listener for service resolution. Can be null.
+ */
+ public void setResolveListener(Channel c, DnsSdResolveListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdResolveListener = b;
+ }
+
+ public void registerService(Channel c, DnsSdServiceInfo serviceInfo) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
+ c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
+ }
+
+ public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
+ }
+
+ public void discoverServices(Channel c, String serviceType) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (c.mDnsSdDiscoveryListener == null) throw new
+ IllegalStateException("Discovery listener needs to be set first");
+ DnsSdServiceInfo s = new DnsSdServiceInfo();
+ s.setServiceType(serviceType);
+ c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
+ }
+
+ public void stopServiceDiscovery(Channel c) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
+ }
+
+ public void resolveService(Channel c, DnsSdServiceInfo serviceInfo) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
+ if (c.mDnsSdResolveListener == null) throw new
+ IllegalStateException("Resolve listener needs to be set first");
+ c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo);
+ }
+
+ /**
+ * Get a reference to NetworkService handler. This is used to establish
+ * an AsyncChannel communication with the service
+ *
+ * @return Messenger pointing to the NetworkService handler
+ */
+ private Messenger getMessenger() {
+ try {
+ return mService.getMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 2223255..3ac1dcc 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -45,4 +45,5 @@
void resetTimeouts();
boolean canMakeReadOnly(int ndefType);
int getMaxTransceiveLength(int technology);
+ boolean getExtendedLengthApdusSupported();
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b7a7bd5..d78e06c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -580,7 +580,18 @@
* and/or {@link #setNdefPushMessageCallback} is called with a null callback,
* then NDEF push will be completely disabled for the specified activity(s).
* This also disables any default NDEF message the Android OS would have
- * otherwise sent on your behalf.
+ * otherwise sent on your behalf for those activity(s).
+ *
+ * <p>If you want to prevent the Android OS from sending default NDEF
+ * messages completely (for all activities), you can include a
+ * <code><meta-data></code> element inside the <code><application></code>
+ * element of your AndroidManifest.xml file, like this:
+ * <pre>{@code
+ * <application ...>
+ * <meta-data android:name="android.nfc.disable_beam_default"
+ * android:value="true" />
+ * </application>
+ * }</pre>
*
* <p>The API allows for multiple activities to be specified at a time,
* but it is strongly recommended to just register one at a time,
@@ -664,7 +675,18 @@
* and/or {@link #setNdefPushMessageCallback} is called with a null callback,
* then NDEF push will be completely disabled for the specified activity(s).
* This also disables any default NDEF message the Android OS would have
- * otherwise sent on your behalf.
+ * otherwise sent on your behalf for those activity(s).
+ *
+ * <p>If you want to prevent the Android OS from sending default NDEF
+ * messages completely (for all activities), you can include a
+ * <code><meta-data></code> element inside the <code><application></code>
+ * element of your AndroidManifest.xml file, like this:
+ * <pre>{@code
+ * <application ...>
+ * <meta-data android:name="android.nfc.disable_beam_default"
+ * android:value="true" />
+ * </application>
+ * }</pre>
*
* <p>The API allows for multiple activities to be specified at a time,
* but it is strongly recommended to just register one at a time,
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 1859877..089b159 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -179,4 +179,27 @@
public int getMaxTransceiveLength() {
return getMaxTransceiveLengthInternal();
}
+
+ /**
+ * <p>Standard APDUs have a 1-byte length field, allowing a maximum of
+ * 255 payload bytes, which results in a maximum APDU length of 261 bytes.
+ *
+ * <p>Extended length APDUs have a 3-byte length field, allowing 65535
+ * payload bytes.
+ *
+ * <p>Some NFC adapters, like the one used in the Nexus S and the Galaxy Nexus
+ * do not support extended length APDUs. They are expected to be well-supported
+ * in the future though. Use this method to check for extended length APDU
+ * support.
+ *
+ * @return whether the NFC adapter on this device supports extended length APDUs.
+ */
+ public boolean isExtendedLengthApduSupported() {
+ try {
+ return mTag.getTagService().getExtendedLengthApdusSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d74ccb8..830a85f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1506,13 +1506,22 @@
public static final String VOLUME_MASTER = "volume_master";
/**
- * Whether the notifications should use the ring volume (value of 1) or a separate
- * notification volume (value of 0). In most cases, users will have this enabled so the
- * notification and ringer volumes will be the same. However, power users can disable this
- * and use the separate notification volume control.
+ * Master volume mute (int 1 = mute, 0 = not muted).
+ *
+ * @hide
+ */
+ public static final String VOLUME_MASTER_MUTE = "volume_master_mute";
+
+ /**
+ * Whether the notifications should use the ring volume (value of 1) or
+ * a separate notification volume (value of 0). In most cases, users
+ * will have this enabled so the notification and ringer volumes will be
+ * the same. However, power users can disable this and use the separate
+ * notification volume control.
* <p>
- * Note: This is a one-off setting that will be removed in the future when there is profile
- * support. For this reason, it is kept hidden from the public APIs.
+ * Note: This is a one-off setting that will be removed in the future
+ * when there is profile support. For this reason, it is kept hidden
+ * from the public APIs.
*
* @hide
* @deprecated
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 9ef2621..b8c692a 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -87,7 +87,7 @@
/**
* System property used to enable or disable hardware rendering profiling.
* The default value of this property is assumed to be false.
- *
+ *
* When profiling is enabled, the adb shell dumpsys gfxinfo command will
* output extra information about the time taken to execute by the last
* frames.
@@ -99,6 +99,20 @@
static final String PROFILE_PROPERTY = "hwui.profile";
/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+ static final String PROFILE_MAXFRAMES_PROPERTY = "hwui.profile.maxframes";
+
+ /**
* System property used to debug EGL configuration choice.
*
* Possible values:
@@ -134,7 +148,7 @@
/**
* Number of frames to profile.
*/
- private static final int PROFILE_MAX_FRAMES = 120;
+ private static final int PROFILE_MAX_FRAMES = 64;
/**
* Number of floats per profiled frame.
@@ -579,7 +593,13 @@
}
if (mProfileEnabled) {
- mProfileData = new float[PROFILE_MAX_FRAMES * PROFILE_FRAME_DATA_COUNT];
+ property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY,
+ Integer.toString(PROFILE_MAX_FRAMES));
+ int maxProfileFrames = Integer.valueOf(property);
+ mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
+ for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ }
} else {
mProfileData = null;
}
@@ -596,9 +616,14 @@
if (mProfileEnabled) {
pw.printf("\n\tDraw\tProcess\tExecute\n");
for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ if (mProfileData[i] < 0) {
+ break;
+ }
pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
mProfileData[i + 2]);
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
}
+ mProfileCurrentFrame = mProfileData.length;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c40a7d5..4b35c72 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -71,6 +71,7 @@
import android.widget.ScrollBarDrawable;
import static android.os.Build.VERSION_CODES.*;
+import static java.lang.Math.max;
import com.android.internal.R;
import com.android.internal.util.Predicate;
@@ -126,7 +127,7 @@
* example, all views will let you set a listener to be notified when the view
* gains or loses focus. You can register such a listener using
* {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
- * Other view subclasses offer more specialized listeners. For example, a Button
+ * Other view subclasses offer more specialized listeners. For example, a Button
* exposes a listener to notify clients when the button is clicked.</li>
* <li><strong>Set visibility:</strong> You can hide or show views using
* {@link #setVisibility(int)}.</li>
@@ -579,6 +580,7 @@
* @attr ref android.R.styleable#View_duplicateParentState
* @attr ref android.R.styleable#View_id
* @attr ref android.R.styleable#View_requiresFadingEdge
+ * @attr ref android.R.styleable#View_fadeScrollbars
* @attr ref android.R.styleable#View_fadingEdgeLength
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
* @attr ref android.R.styleable#View_fitsSystemWindows
@@ -624,6 +626,7 @@
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
* @attr ref android.R.styleable#View_soundEffectsEnabled
* @attr ref android.R.styleable#View_tag
+ * @attr ref android.R.styleable#View_textAlignment
* @attr ref android.R.styleable#View_transformPivotX
* @attr ref android.R.styleable#View_transformPivotY
* @attr ref android.R.styleable#View_translationX
@@ -1827,17 +1830,17 @@
public static final int TEXT_DIRECTION_LOCALE = 5;
/**
+ * Default text direction is inherited
+ */
+ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+
+ /**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
* @hide
*/
static final int TEXT_DIRECTION_MASK_SHIFT = 6;
/**
- * Default text direction is inherited
- */
- protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
-
- /**
* Mask for use with private flags indicating bits used for text direction.
* @hide
*/
@@ -1882,6 +1885,113 @@
static final int TEXT_DIRECTION_RESOLVED_DEFAULT =
TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
+ /*
+ * Default text alignment. The text alignment of this View is inherited from its parent.
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_INHERIT = 0;
+
+ /**
+ * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL,
+ * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_GRAVITY = 1;
+
+ /**
+ * Align to the start of the paragraph, e.g. ALIGN_NORMAL.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_START = 2;
+
+ /**
+ * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_END = 3;
+
+ /**
+ * Center the paragraph, e.g. ALIGN_CENTER.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_CENTER = 4;
+
+ /**
+ * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_RIGHT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_START = 5;
+
+ /**
+ * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_LEFT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_END = 6;
+
+ /**
+ * Default text alignment is inherited
+ */
+ protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK_SHIFT = 13;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK = 0x00000007 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Array of text direction flags for mapping attribute "textAlignment" to correct
+ * flag value.
+ * @hide
+ */
+ private static final int[] TEXT_ALIGNMENT_FLAGS = {
+ TEXT_ALIGNMENT_INHERIT << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_END << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_CENTER << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_END << TEXT_ALIGNMENT_MASK_SHIFT
+ };
+
+ /**
+ * Indicates whether the view text alignment has been resolved.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED = 0x00000008 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Bit shift to get the resolved text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT = 17;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK = 0x00000007 << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
+ /**
+ * Indicates whether if the view text alignment has been resolved to gravity
+ */
+ public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
/* End of masks for mPrivateFlags2 */
@@ -1926,7 +2036,7 @@
* system UI to enter an unobtrusive "low profile" mode.
*
* <p>This is for use in games, book readers, video players, or any other
- * "immersive" application where the usual system chrome is deemed too distracting.
+ * "immersive" application where the usual system chrome is deemed too distracting.
*
* <p>In low profile mode, the status bar and/or navigation icons may dim.
*
@@ -1942,7 +2052,7 @@
* {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
* (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause
* those to disappear. This is useful (in conjunction with the
- * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
+ * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
* {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
* window flags) for displaying content using every last pixel on the display.
*
@@ -2339,7 +2449,7 @@
*/
private int mPrevWidth = -1;
private int mPrevHeight = -1;
-
+
/**
* The degrees rotation around the vertical axis through the pivot point.
*/
@@ -2546,7 +2656,7 @@
*/
int mOldHeightMeasureSpec = Integer.MIN_VALUE;
- private Drawable mBGDrawable;
+ private Drawable mBackground;
private int mBackgroundResource;
private boolean mBackgroundSizeChanged;
@@ -2620,7 +2730,7 @@
/**
* Set to true when drawing cache is enabled and cannot be created.
- *
+ *
* @hide
*/
public boolean mCachingFailed;
@@ -2841,7 +2951,8 @@
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
// Set layout and text direction defaults
mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
- (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT);
+ (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
+ (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = -1;
@@ -3222,6 +3333,13 @@
mPrivateFlags2 |= TEXT_DIRECTION_FLAGS[textDirection];
}
break;
+ case R.styleable.View_textAlignment:
+ // Clear any text alignment flag already set
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ // Set the text alignment flag depending on the value of the attribute
+ final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
+ mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
+ break;
}
}
@@ -3230,7 +3348,7 @@
setOverScrollMode(overScrollMode);
if (background != null) {
- setBackgroundDrawable(background);
+ setBackground(background);
}
// Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
@@ -3494,6 +3612,11 @@
}
}
+ private ScrollabilityCache getScrollCache() {
+ initScrollCache();
+ return mScrollCache;
+ }
+
/**
* Set the position of the vertical scroll bar. Should be one of
* {@link #SCROLLBAR_POSITION_DEFAULT}, {@link #SCROLLBAR_POSITION_LEFT} or
@@ -4562,11 +4685,26 @@
}
/**
+ * Indicates whether this view is one of the set of scrollable containers in
+ * its window.
+ *
+ * @return whether this view is one of the set of scrollable containers in
+ * its window
+ *
+ * @attr ref android.R.styleable#View_isScrollContainer
+ */
+ public boolean isScrollContainer() {
+ return (mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0;
+ }
+
+ /**
* Change whether this view is one of the set of scrollable containers in
* its window. This will be used to determine whether the window can
* resize or must pan when a soft input area is open -- scrollable
* containers allow the window to use resize mode since the container
* will appropriately shrink.
+ *
+ * @attr ref android.R.styleable#View_isScrollContainer
*/
public void setScrollContainer(boolean isScrollContainer) {
if (isScrollContainer) {
@@ -4898,7 +5036,7 @@
@RemotableViewMethod
public void setVisibility(int visibility) {
setFlags(visibility, VISIBILITY_MASK);
- if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
+ if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
}
/**
@@ -5279,7 +5417,7 @@
* {@link #setPressed(boolean)} is explicitly called, only clickable views can enter
* the pressed state.
*
- * @see #setPressed(boolean)
+ * @see #setPressed(boolean)
* @see #isClickable()
* @see #setClickable(boolean)
*
@@ -5965,7 +6103,7 @@
/**
* Dispatch a hover event.
* <p>
- * Do not call this method directly.
+ * Do not call this method directly.
* Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
* </p>
*
@@ -6152,7 +6290,7 @@
*
* @param visibility The new visibility of the window.
*
- * @see #onWindowVisibilityChanged(int)
+ * @see #onWindowVisibilityChanged(int)
*/
public void dispatchWindowVisibilityChanged(int visibility) {
onWindowVisibilityChanged(visibility);
@@ -6228,7 +6366,7 @@
*
* @param newConfig The new resource configuration.
*
- * @see #onConfigurationChanged(android.content.res.Configuration)
+ * @see #onConfigurationChanged(android.content.res.Configuration)
*/
public void dispatchConfigurationChanged(Configuration newConfig) {
onConfigurationChanged(newConfig);
@@ -7096,7 +7234,7 @@
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
- if (mBGDrawable != null) {
+ if (mBackground != null) {
mPrivateFlags &= ~SKIP_DRAW;
mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
} else {
@@ -7484,39 +7622,39 @@
* views are drawn) from the camera to this view. The camera's distance
* affects 3D transformations, for instance rotations around the X and Y
* axis. If the rotationX or rotationY properties are changed and this view is
- * large (more than half the size of the screen), it is recommended to always
+ * large (more than half the size of the screen), it is recommended to always
* use a camera distance that's greater than the height (X axis rotation) or
* the width (Y axis rotation) of this view.</p>
- *
+ *
* <p>The distance of the camera from the view plane can have an affect on the
* perspective distortion of the view when it is rotated around the x or y axis.
* For example, a large distance will result in a large viewing angle, and there
* will not be much perspective distortion of the view as it rotates. A short
- * distance may cause much more perspective distortion upon rotation, and can
+ * distance may cause much more perspective distortion upon rotation, and can
* also result in some drawing artifacts if the rotated view ends up partially
* behind the camera (which is why the recommendation is to use a distance at
* least as far as the size of the view, if the view is to be rotated.)</p>
- *
+ *
* <p>The distance is expressed in "depth pixels." The default distance depends
* on the screen density. For instance, on a medium density display, the
* default distance is 1280. On a high density display, the default distance
* is 1920.</p>
- *
+ *
* <p>If you want to specify a distance that leads to visually consistent
* results across various densities, use the following formula:</p>
* <pre>
* float scale = context.getResources().getDisplayMetrics().density;
* view.setCameraDistance(distance * scale);
* </pre>
- *
+ *
* <p>The density scale factor of a high density display is 1.5,
* and 1920 = 1280 * 1.5.</p>
- *
+ *
* @param distance The distance in "depth pixels", if negative the opposite
* value is used
- *
- * @see #setRotationX(float)
- * @see #setRotationY(float)
+ *
+ * @see #setRotationX(float)
+ * @see #setRotationY(float)
*/
public void setCameraDistance(float distance) {
invalidateViewProperty(true, false);
@@ -7541,10 +7679,10 @@
/**
* The degrees that the view is rotated around the pivot point.
*
- * @see #setRotation(float)
+ * @see #setRotation(float)
* @see #getPivotX()
* @see #getPivotY()
- *
+ *
* @return The degrees of rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7557,12 +7695,12 @@
* result in clockwise rotation.
*
* @param rotation The degrees of rotation.
- *
- * @see #getRotation()
+ *
+ * @see #getRotation()
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationX(float)
- * @see #setRotationY(float)
+ * @see #setRotationX(float)
+ * @see #setRotationY(float)
*
* @attr ref android.R.styleable#View_rotation
*/
@@ -7586,8 +7724,8 @@
*
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationY(float)
- *
+ * @see #setRotationY(float)
+ *
* @return The degrees of Y rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7599,18 +7737,18 @@
* Sets the degrees that the view is rotated around the vertical axis through the pivot point.
* Increasing values result in counter-clockwise rotation from the viewpoint of looking
* down the y axis.
- *
+ *
* When rotating large views, it is recommended to adjust the camera distance
* accordingly. Refer to {@link #setCameraDistance(float)} for more information.
*
* @param rotationY The degrees of Y rotation.
- *
- * @see #getRotationY()
+ *
+ * @see #getRotationY()
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotation(float)
- * @see #setRotationX(float)
- * @see #setCameraDistance(float)
+ * @see #setRotationX(float)
+ * @see #setCameraDistance(float)
*
* @attr ref android.R.styleable#View_rotationY
*/
@@ -7633,8 +7771,8 @@
*
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationX(float)
- *
+ * @see #setRotationX(float)
+ *
* @return The degrees of X rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7646,18 +7784,18 @@
* Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
* Increasing values result in clockwise rotation from the viewpoint of looking down the
* x axis.
- *
+ *
* When rotating large views, it is recommended to adjust the camera distance
* accordingly. Refer to {@link #setCameraDistance(float)} for more information.
*
* @param rotationX The degrees of X rotation.
- *
- * @see #getRotationX()
+ *
+ * @see #getRotationX()
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotation(float)
- * @see #setRotationY(float)
- * @see #setCameraDistance(float)
+ * @see #setRotationY(float)
+ * @see #setCameraDistance(float)
*
* @attr ref android.R.styleable#View_rotationX
*/
@@ -7762,6 +7900,8 @@
* @see #getScaleY()
* @see #getPivotY()
* @return The x location of the pivot point.
+ *
+ * @attr ref android.R.styleable#View_transformPivotX
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getPivotX() {
@@ -7807,6 +7947,8 @@
* @see #getScaleY()
* @see #getPivotY()
* @return The y location of the pivot point.
+ *
+ * @attr ref android.R.styleable#View_transformPivotY
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getPivotY() {
@@ -9022,7 +9164,7 @@
// - Background is opaque
// - Doesn't have scrollbars or scrollbars are inside overlay
- if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) {
+ if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~OPAQUE_BACKGROUND;
@@ -9070,7 +9212,7 @@
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9094,7 +9236,7 @@
* <p>Causes the Runnable to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9168,7 +9310,7 @@
/**
* <p>Removes the specified Runnable from the message queue.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9200,7 +9342,7 @@
*
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
- *
+ *
* @see #invalidate()
*/
public void postInvalidate() {
@@ -9210,7 +9352,7 @@
/**
* <p>Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Use this to invalidate the View from a non-UI thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9229,7 +9371,7 @@
/**
* <p>Cause an invalidate to happen on a subsequent cycle through the event
* loop. Waits for the specified amount of time.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9248,7 +9390,7 @@
/**
* <p>Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Waits for the specified amount of time.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9358,6 +9500,7 @@
* otherwise
*
* @see #setHorizontalFadingEdgeEnabled(boolean)
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public boolean isHorizontalFadingEdgeEnabled() {
@@ -9373,6 +9516,7 @@
* horizontally
*
* @see #isHorizontalFadingEdgeEnabled()
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) {
@@ -9393,6 +9537,7 @@
* otherwise
*
* @see #setVerticalFadingEdgeEnabled(boolean)
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public boolean isVerticalFadingEdgeEnabled() {
@@ -9408,6 +9553,7 @@
* vertically
*
* @see #isVerticalFadingEdgeEnabled()
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) {
@@ -9550,6 +9696,7 @@
*
* @param fadeScrollbars wheter to enable fading
*
+ * @attr ref android.R.styleable#View_fadeScrollbars
*/
public void setScrollbarFadingEnabled(boolean fadeScrollbars) {
initScrollCache();
@@ -9567,12 +9714,86 @@
* Returns true if scrollbars will fade when this view is not scrolling
*
* @return true if scrollbar fading is enabled
+ *
+ * @attr ref android.R.styleable#View_fadeScrollbars
*/
public boolean isScrollbarFadingEnabled() {
return mScrollCache != null && mScrollCache.fadeScrollBars;
}
/**
+ *
+ * Returns the delay before scrollbars fade.
+ *
+ * @return the delay before scrollbars fade
+ *
+ * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+ */
+ public int getScrollBarDefaultDelayBeforeFade() {
+ return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
+ mScrollCache.scrollBarDefaultDelayBeforeFade;
+ }
+
+ /**
+ * Define the delay before scrollbars fade.
+ *
+ * @param scrollBarDefaultDelayBeforeFade - the delay before scrollbars fade
+ *
+ * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+ */
+ public void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) {
+ getScrollCache().scrollBarDefaultDelayBeforeFade = scrollBarDefaultDelayBeforeFade;
+ }
+
+ /**
+ *
+ * Returns the scrollbar fade duration.
+ *
+ * @return the scrollbar fade duration
+ *
+ * @attr ref android.R.styleable#View_scrollbarFadeDuration
+ */
+ public int getScrollBarFadeDuration() {
+ return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
+ mScrollCache.scrollBarFadeDuration;
+ }
+
+ /**
+ * Define the scrollbar fade duration.
+ *
+ * @param scrollBarFadeDuration - the scrollbar fade duration
+ *
+ * @attr ref android.R.styleable#View_scrollbarFadeDuration
+ */
+ public void setScrollBarFadeDuration(int scrollBarFadeDuration) {
+ getScrollCache().scrollBarFadeDuration = scrollBarFadeDuration;
+ }
+
+ /**
+ *
+ * Returns the scrollbar size.
+ *
+ * @return the scrollbar size
+ *
+ * @attr ref android.R.styleable#View_scrollbarSize
+ */
+ public int getScrollBarSize() {
+ return mScrollCache == null ? ViewConfiguration.getScrollBarSize() :
+ mScrollCache.scrollBarSize;
+ }
+
+ /**
+ * Define the scrollbar size.
+ *
+ * @param scrollBarSize - the scrollbar size
+ *
+ * @attr ref android.R.styleable#View_scrollbarSize
+ */
+ public void setScrollBarSize(int scrollBarSize) {
+ getScrollCache().scrollBarSize = scrollBarSize;
+ }
+
+ /**
* <p>Specify the style of the scrollbars. The scrollbars can be overlaid or
* inset. When inset, they add to the padding of the view. And the scrollbars
* can be drawn inside the padding area or on the edge of the view. For example,
@@ -9588,6 +9809,8 @@
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
+ *
+ * @attr ref android.R.styleable#View_scrollbarStyle
*/
public void setScrollBarStyle(int style) {
if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
@@ -9604,6 +9827,8 @@
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
+ *
+ * @attr ref android.R.styleable#View_scrollbarStyle
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = SCROLLBARS_INSIDE_OVERLAY, to = "INSIDE_OVERLAY"),
@@ -9989,6 +10214,7 @@
resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
+ resolveTextAlignment();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -10218,6 +10444,7 @@
mCurrentAnimation = null;
resetResolvedLayoutDirection();
+ resetResolvedTextAlignment();
}
/**
@@ -10348,9 +10575,9 @@
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
- * @see #onSaveInstanceState()
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
@@ -10363,9 +10590,9 @@
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #onSaveInstanceState()
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
@@ -10399,9 +10626,9 @@
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save. The
* default implementation returns null.
- * @see #onRestoreInstanceState(android.os.Parcelable)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #setSaveEnabled(boolean)
*/
protected Parcelable onSaveInstanceState() {
@@ -10414,9 +10641,9 @@
*
* @param container The SparseArray which holds previously frozen states.
*
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
- * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
@@ -10430,9 +10657,9 @@
*
* @param container The SparseArray which holds previously saved state.
*
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
@@ -10458,9 +10685,9 @@
* @param state The frozen state that had previously been returned by
* {@link #onSaveInstanceState}.
*
- * @see #onSaveInstanceState()
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
*/
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= SAVE_STATE_CALLED;
@@ -10614,7 +10841,7 @@
* {@link #LAYER_TYPE_HARDWARE}
*
* @see #setLayerType(int, android.graphics.Paint)
- * @see #buildLayer()
+ * @see #buildLayer()
* @see #LAYER_TYPE_NONE
* @see #LAYER_TYPE_SOFTWARE
* @see #LAYER_TYPE_HARDWARE
@@ -10627,14 +10854,14 @@
* Forces this view's layer to be created and this view to be rendered
* into its layer. If this view's layer type is set to {@link #LAYER_TYPE_NONE},
* invoking this method will have no effect.
- *
+ *
* This method can for instance be used to render a view into its layer before
* starting an animation. If this view is complex, rendering into the layer
* before starting the animation will avoid skipping frames.
- *
+ *
* @throws IllegalStateException If this view is not attached to a window
- *
- * @see #setLayerType(int, android.graphics.Paint)
+ *
+ * @see #setLayerType(int, android.graphics.Paint)
*/
public void buildLayer() {
if (mLayerType == LAYER_TYPE_NONE) return;
@@ -10656,7 +10883,7 @@
break;
}
}
-
+
// Make sure the HardwareRenderer.validate() was invoked before calling this method
void flushLayer() {
if (mLayerType == LAYER_TYPE_HARDWARE && mHardwareLayer != null) {
@@ -10675,7 +10902,7 @@
!mAttachInfo.mHardwareRenderer.isEnabled()) {
return null;
}
-
+
if (!mAttachInfo.mHardwareRenderer.validate()) return null;
final int width = mRight - mLeft;
@@ -10709,10 +10936,10 @@
/**
* Destroys this View's hardware layer if possible.
- *
+ *
* @return True if the layer was destroyed, false otherwise.
- *
- * @see #setLayerType(int, android.graphics.Paint)
+ *
+ * @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_HARDWARE
*/
boolean destroyLayer(boolean valid) {
@@ -10736,11 +10963,11 @@
* Destroys all hardware rendering resources. This method is invoked
* when the system needs to reclaim resources. Upon execution of this
* method, you should free any OpenGL resources created by the view.
- *
+ *
* Note: you <strong>must</strong> call
* <code>super.destroyHardwareResources()</code> when overriding
* this method.
- *
+ *
* @hide
*/
protected void destroyHardwareResources() {
@@ -11451,17 +11678,17 @@
if (offsetRequired) top += getTopPaddingOffset();
return top;
}
-
+
/**
* @hide
* @param offsetRequired
*/
protected int getFadeHeight(boolean offsetRequired) {
int padding = mPaddingTop;
- if (offsetRequired) padding += getTopPaddingOffset();
+ if (offsetRequired) padding += getTopPaddingOffset();
return mBottom - mTop - mPaddingBottom - padding;
}
-
+
/**
* <p>Indicates whether this view is attached to a hardware accelerated
* window or not.</p>
@@ -11962,7 +12189,7 @@
int saveCount;
if (!dirtyOpaque) {
- final Drawable background = mBGDrawable;
+ final Drawable background = mBackground;
if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -12036,7 +12263,7 @@
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
- final float fadeHeight = scrollabilityCache.fadingEdgeLength;
+ final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
@@ -12143,8 +12370,8 @@
* optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
* should be set to 0xFF.
*
- * @see #setVerticalFadingEdgeEnabled(boolean)
- * @see #setHorizontalFadingEdgeEnabled(boolean)
+ * @see #setVerticalFadingEdgeEnabled(boolean)
+ * @see #setHorizontalFadingEdgeEnabled(boolean)
*
* @return The known solid color background for this view, or 0 if the color may vary
*/
@@ -12493,7 +12720,7 @@
* @param who the Drawable to query
*/
public int getResolvedLayoutDirection(Drawable who) {
- return (who == mBGDrawable) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
+ return (who == mBackground) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
}
/**
@@ -12512,11 +12739,11 @@
* @return boolean If true than the Drawable is being displayed in the
* view; else false and it is not allowed to animate.
*
- * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
- * @see #drawableStateChanged()
+ * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
+ * @see #drawableStateChanged()
*/
protected boolean verifyDrawable(Drawable who) {
- return who == mBGDrawable;
+ return who == mBackground;
}
/**
@@ -12526,10 +12753,10 @@
* <p>Be sure to call through to the superclass when overriding this
* function.
*
- * @see Drawable#setState(int[])
+ * @see Drawable#setState(int[])
*/
protected void drawableStateChanged() {
- Drawable d = mBGDrawable;
+ Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
@@ -12559,9 +12786,9 @@
*
* @return The current drawable state
*
- * @see Drawable#setState(int[])
- * @see #drawableStateChanged()
- * @see #onCreateDrawableState(int)
+ * @see Drawable#setState(int[])
+ * @see #drawableStateChanged()
+ * @see #onCreateDrawableState(int)
*/
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {
@@ -12586,7 +12813,7 @@
* @return Returns an array holding the current {@link Drawable} state of
* the view.
*
- * @see #mergeDrawableStates(int[], int[])
+ * @see #mergeDrawableStates(int[], int[])
*/
protected int[] onCreateDrawableState(int extraSpace) {
if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
@@ -12662,7 +12889,7 @@
* @return As a convenience, the <var>baseState</var> array you originally
* passed into the function is returned.
*
- * @see #onCreateDrawableState(int)
+ * @see #onCreateDrawableState(int)
*/
protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
final int N = baseState.length;
@@ -12679,8 +12906,8 @@
* on all Drawable objects associated with this view.
*/
public void jumpDrawablesToCurrentState() {
- if (mBGDrawable != null) {
- mBGDrawable.jumpToCurrentState();
+ if (mBackground != null) {
+ mBackground.jumpToCurrentState();
}
}
@@ -12690,10 +12917,10 @@
*/
@RemotableViewMethod
public void setBackgroundColor(int color) {
- if (mBGDrawable instanceof ColorDrawable) {
- ((ColorDrawable) mBGDrawable).setColor(color);
+ if (mBackground instanceof ColorDrawable) {
+ ((ColorDrawable) mBackground).setColor(color);
} else {
- setBackgroundDrawable(new ColorDrawable(color));
+ setBackground(new ColorDrawable(color));
}
}
@@ -12701,6 +12928,7 @@
* Set the background to a given resource. The resource should refer to
* a Drawable object or 0 to remove the background.
* @param resid The identifier of the resource.
+ *
* @attr ref android.R.styleable#View_background
*/
@RemotableViewMethod
@@ -12713,7 +12941,7 @@
if (resid != 0) {
d = mResources.getDrawable(resid);
}
- setBackgroundDrawable(d);
+ setBackground(d);
mBackgroundResource = resid;
}
@@ -12725,11 +12953,19 @@
* touched. If setting the padding is desired, please use
* {@link #setPadding(int, int, int, int)}.
*
- * @param d The Drawable to use as the background, or null to remove the
+ * @param background The Drawable to use as the background, or null to remove the
* background
*/
- public void setBackgroundDrawable(Drawable d) {
- if (d == mBGDrawable) {
+ public void setBackground(Drawable background) {
+ setBackgroundDrawable(background);
+ }
+
+ /**
+ * @deprecated use {@link #setBackground(Drawable)} instead
+ */
+ @Deprecated
+ public void setBackgroundDrawable(Drawable background) {
+ if (background == mBackground) {
return;
}
@@ -12741,19 +12977,19 @@
* Regardless of whether we're setting a new background or not, we want
* to clear the previous drawable.
*/
- if (mBGDrawable != null) {
- mBGDrawable.setCallback(null);
- unscheduleDrawable(mBGDrawable);
+ if (mBackground != null) {
+ mBackground.setCallback(null);
+ unscheduleDrawable(mBackground);
}
- if (d != null) {
+ if (background != null) {
Rect padding = sThreadLocal.get();
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
- if (d.getPadding(padding)) {
- switch (d.getResolvedLayoutDirectionSelf()) {
+ if (background.getPadding(padding)) {
+ switch (background.getResolvedLayoutDirectionSelf()) {
case LAYOUT_DIRECTION_RTL:
setPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
@@ -12765,17 +13001,17 @@
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
- if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||
- mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {
+ if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
+ mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
- d.setCallback(this);
- if (d.isStateful()) {
- d.setState(getDrawableState());
+ background.setCallback(this);
+ if (background.isStateful()) {
+ background.setState(getDrawableState());
}
- d.setVisible(getVisibility() == VISIBLE, false);
- mBGDrawable = d;
+ background.setVisible(getVisibility() == VISIBLE, false);
+ mBackground = background;
if ((mPrivateFlags & SKIP_DRAW) != 0) {
mPrivateFlags &= ~SKIP_DRAW;
@@ -12784,7 +13020,7 @@
}
} else {
/* Remove the background */
- mBGDrawable = null;
+ mBackground = null;
if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {
/*
@@ -12820,10 +13056,15 @@
/**
* Gets the background drawable
+ *
* @return The drawable used as the background for this view, if any.
+ *
+ * @see #setBackground(Drawable)
+ *
+ * @attr ref android.R.styleable#View_background
*/
public Drawable getBackground() {
- return mBGDrawable;
+ return mBackground;
}
/**
@@ -13364,8 +13605,8 @@
* number.
*
* @see #NO_ID
- * @see #getId()
- * @see #findViewById(int)
+ * @see #getId()
+ * @see #findViewById(int)
*
* @param id a number used to identify the view
*
@@ -13404,8 +13645,8 @@
* @return a positive integer used to identify the view or {@link #NO_ID}
* if the view has no ID
*
- * @see #setId(int)
- * @see #findViewById(int)
+ * @see #setId(int)
+ * @see #findViewById(int)
* @attr ref android.R.styleable#View_id
*/
@ViewDebug.CapturedViewProperty
@@ -13917,16 +14158,8 @@
* @return The suggested minimum height of the view.
*/
protected int getSuggestedMinimumHeight() {
- int suggestedMinHeight = mMinHeight;
+ return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
- if (mBGDrawable != null) {
- final int bgMinHeight = mBGDrawable.getMinimumHeight();
- if (suggestedMinHeight < bgMinHeight) {
- suggestedMinHeight = bgMinHeight;
- }
- }
-
- return suggestedMinHeight;
}
/**
@@ -13941,16 +14174,20 @@
* @return The suggested minimum width of the view.
*/
protected int getSuggestedMinimumWidth() {
- int suggestedMinWidth = mMinWidth;
+ return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
+ }
- if (mBGDrawable != null) {
- final int bgMinWidth = mBGDrawable.getMinimumWidth();
- if (suggestedMinWidth < bgMinWidth) {
- suggestedMinWidth = bgMinWidth;
- }
- }
-
- return suggestedMinWidth;
+ /**
+ * Returns the minimum height of the view.
+ *
+ * @return the minimum height the view will try to be.
+ *
+ * @see #setMinimumHeight(int)
+ *
+ * @attr ref android.R.styleable#View_minHeight
+ */
+ public int getMinimumHeight() {
+ return mMinHeight;
}
/**
@@ -13959,9 +14196,27 @@
* constrains it with less available height).
*
* @param minHeight The minimum height the view will try to be.
+ *
+ * @see #getMinimumHeight()
+ *
+ * @attr ref android.R.styleable#View_minHeight
*/
public void setMinimumHeight(int minHeight) {
mMinHeight = minHeight;
+ requestLayout();
+ }
+
+ /**
+ * Returns the minimum width of the view.
+ *
+ * @return the minimum width the view will try to be.
+ *
+ * @see #setMinimumWidth(int)
+ *
+ * @attr ref android.R.styleable#View_minWidth
+ */
+ public int getMinimumWidth() {
+ return mMinWidth;
}
/**
@@ -13970,9 +14225,15 @@
* constrains it with less available width).
*
* @param minWidth The minimum width the view will try to be.
+ *
+ * @see #getMinimumWidth()
+ *
+ * @attr ref android.R.styleable#View_minWidth
*/
public void setMinimumWidth(int minWidth) {
mMinWidth = minWidth;
+ requestLayout();
+
}
/**
@@ -14091,11 +14352,11 @@
getLocationInWindow(location);
region.op(location[0], location[1], location[0] + mRight - mLeft,
location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
- } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) {
+ } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBackground != null) {
// The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable
// exists, so we remove the background drawable's non-transparent
// parts from this transparent region.
- applyDrawableToTransparentRegion(mBGDrawable, region);
+ applyDrawableToTransparentRegion(mBackground, region);
}
}
return true;
@@ -14766,7 +15027,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
@ViewDebug.ExportedProperty(category = "text", mapping = {
@ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -14790,7 +15051,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public void setTextDirection(int textDirection) {
if (getTextDirection() != textDirection) {
@@ -14799,6 +15060,7 @@
resetResolvedTextDirection();
// Set the new text direction
mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
+ // Refresh
requestLayout();
invalidate(true);
}
@@ -14818,7 +15080,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public int getResolvedTextDirection() {
// The text direction will be resolved only if needed
@@ -14927,6 +15189,199 @@
public void onResolvedTextDirectionReset() {
}
+ /**
+ * Return the value specifying the text alignment or policy that was set with
+ * {@link #setTextAlignment(int)}.
+ *
+ * @return the defined text alignment. It can be one of:
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getTextAlignment() {
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_MASK) >> TEXT_ALIGNMENT_MASK_SHIFT;
+ }
+
+ /**
+ * Set the text alignment.
+ *
+ * @param textAlignment The text alignment to set. Should be one of
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ *
+ * @attr ref android.R.styleable#View_textAlignment
+ */
+ public void setTextAlignment(int textAlignment) {
+ if (textAlignment != getTextAlignment()) {
+ // Reset the current and resolved text alignment
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ resetResolvedTextAlignment();
+ // Set the new text alignment
+ mPrivateFlags2 |= ((textAlignment << TEXT_ALIGNMENT_MASK_SHIFT) & TEXT_ALIGNMENT_MASK);
+ // Refresh
+ requestLayout();
+ invalidate(true);
+ }
+ }
+
+ /**
+ * Return the resolved text alignment.
+ *
+ * The resolved text alignment. This needs resolution if the value is
+ * TEXT_ALIGNMENT_INHERIT. The resolution matches {@link #setTextAlignment(int)} if it is
+ * not TEXT_ALIGNMENT_INHERIT, otherwise resolution proceeds up the parent chain of the view.
+ *
+ * @return the resolved text alignment. Returns one of:
+ *
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getResolvedTextAlignment() {
+ // If text alignment is not resolved, then resolve it
+ if ((mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED) != TEXT_ALIGNMENT_RESOLVED) {
+ resolveTextAlignment();
+ }
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED_MASK) >> TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+ }
+
+ /**
+ * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when
+ * resolution is done.
+ */
+ public void resolveTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+
+ if (hasRtlSupport()) {
+ // Set resolved text alignment flag depending on text alignment flag
+ final int textAlignment = getTextAlignment();
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_INHERIT:
+ // Check if we can resolve the text alignment
+ if (canResolveLayoutDirection() && mParent instanceof View) {
+ View view = (View) mParent;
+
+ final int parentResolvedTextAlignment = view.getResolvedTextAlignment();
+ switch (parentResolvedTextAlignment) {
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as the parent resolved
+ // text alignment
+ mPrivateFlags2 |=
+ (parentResolvedTextAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ }
+ else {
+ // We cannot do the resolution if there is no parent so use the default
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ break;
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as text alignment
+ mPrivateFlags2 |= (textAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ } else {
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+
+ // Set the resolved
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED;
+ onResolvedTextAlignmentChanged();
+ }
+
+ /**
+ * Check if text alignment resolution can be done.
+ *
+ * @return true if text alignment resolution can be done otherwise return false.
+ */
+ public boolean canResolveTextAlignment() {
+ switch (getTextAlignment()) {
+ case TEXT_DIRECTION_INHERIT:
+ return (mParent != null);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Called when text alignment has been resolved. Subclasses that care about text alignment
+ * resolution should override this method.
+ *
+ * The default implementation does nothing.
+ */
+ public void onResolvedTextAlignmentChanged() {
+ }
+
+ /**
+ * Reset resolved text alignment. Text alignment can be resolved with a call to
+ * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when
+ * reset is done.
+ */
+ public void resetResolvedTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+ onResolvedTextAlignmentReset();
+ }
+
+ /**
+ * Called when text alignment is reset. Subclasses that care about text alignment reset should
+ * override this method and do a reset of the text alignment of their children. The default
+ * implementation does nothing.
+ */
+ public void onResolvedTextAlignmentReset() {
+ }
+
//
// Properties
//
@@ -15419,7 +15874,7 @@
* visibility. This reports <strong>global</strong> changes to the system UI
* state, not just what the application is requesting.
*
- * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
+ * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
*/
public interface OnSystemUiVisibilityChangeListener {
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d5c783f..ae5debe 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5056,6 +5056,18 @@
}
}
+ @Override
+ public void onResolvedTextAlignmentReset() {
+ // Take care of resetting the children resolution too
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getTextAlignment() == TEXT_ALIGNMENT_INHERIT) {
+ child.resetResolvedTextAlignment();
+ }
+ }
+ }
+
/**
* Return true if the pressed state should be delayed for children or descendants of this
* ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 880dc34..cbff58c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1609,7 +1609,6 @@
private boolean mCancelled;
public void run() {
- Log.d("GILLES", "blinking !!!");
if (mCancelled) {
return;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d2a1755..9867e47 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5340,24 +5340,63 @@
physicalWidth, false);
}
+ @Override
+ public void onResolvedLayoutDirectionReset() {
+ if (mLayoutAlignment != null) {
+ int resolvedTextAlignment = getResolvedTextAlignment();
+ if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+ resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+ mLayoutAlignment = null;
+ }
+ }
+ }
+
private Layout.Alignment getLayoutAlignment() {
if (mLayoutAlignment == null) {
- switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
- case Gravity.START:
+ int textAlign = getResolvedTextAlignment();
+ switch (textAlign) {
+ case TEXT_ALIGNMENT_GRAVITY:
+ switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.START:
+ mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ case Gravity.END:
+ mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ case Gravity.LEFT:
+ mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
+ break;
+ case Gravity.RIGHT:
+ mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
+ break;
+ default:
+ mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ }
+ break;
+ case TEXT_ALIGNMENT_TEXT_START:
mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
break;
- case Gravity.END:
+ case TEXT_ALIGNMENT_TEXT_END:
mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
break;
- case Gravity.LEFT:
- mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
- break;
- case Gravity.RIGHT:
- mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
- break;
- case Gravity.CENTER_HORIZONTAL:
+ case TEXT_ALIGNMENT_CENTER:
mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
break;
+ case TEXT_ALIGNMENT_VIEW_START:
+ mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+ break;
+ case TEXT_ALIGNMENT_VIEW_END:
+ mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+ break;
+ case TEXT_ALIGNMENT_INHERIT:
+ // This should never happen as we have already resolved the text alignment
+ // but better safe than sorry so we just fall through
default:
mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
break;
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index d462d7f..7c2b1b5 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -49,7 +49,7 @@
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
-
public static final int BASE_DNS_PINGER = 0x00050000;
+ public static final int BASE_NSD_MANAGER = 0x00060000;
//TODO: define all used protocols
}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5e73a5f..3c27caf 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -236,7 +236,7 @@
0, 0, width, height, bitmap);
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);
}
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
@@ -248,7 +248,7 @@
return NULL;
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);
}
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
@@ -407,7 +407,7 @@
bitmap->unlockPixels();
blob.release();
- return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);
+ return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
@@ -485,7 +485,7 @@
env->ReleaseIntArrayElements(offsetXY, array, 0);
}
- return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);
+ return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index dcd1d28..dd59444 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -35,6 +35,7 @@
jfieldID gOptions_mCancelID;
jfieldID gOptions_bitmapFieldID;
jfieldID gBitmap_nativeBitmapFieldID;
+jfieldID gBitmap_layoutBoundsFieldID;
#if 0
#define TRACE_BITMAP(code) code
@@ -276,7 +277,7 @@
}
jbyteArray ninePatchChunk = NULL;
- if (peeker.fPatchIsValid) {
+ if (peeker.fPatch != NULL) {
if (willScale) {
scaleNinePatchChunk(peeker.fPatch, scale);
}
@@ -296,6 +297,18 @@
env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
}
+ jintArray layoutBounds = NULL;
+ if (peeker.fLayoutBounds != NULL) {
+ layoutBounds = env->NewIntArray(4);
+ if (layoutBounds == NULL) {
+ return nullObjectReturn("layoutBounds == null");
+ }
+
+ env->SetIntArrayRegion(layoutBounds, 0, 4, (jint*) peeker.fLayoutBounds);
+ if (javaBitmap != NULL) {
+ env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
+ }
+ }
// detach bitmap from its autodeleter, since we want to own it now
adb.detach();
@@ -321,7 +334,7 @@
}
if (padding) {
- if (peeker.fPatchIsValid) {
+ if (peeker.fPatch != NULL) {
GraphicsJNI::set_jrect(env, padding,
peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
@@ -350,7 +363,7 @@
}
// now create the java bitmap
return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
- isMutable, ninePatchChunk);
+ isMutable, ninePatchChunk, layoutBounds, -1);
}
static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
@@ -576,7 +589,7 @@
jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
SkASSERT(bitmap_class);
gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
-
+ gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
int ret = AndroidRuntime::registerNativeMethods(env,
"android/graphics/BitmapFactory$Options",
gOptionsMethods,
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 682877a..dd8e84f 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -244,7 +244,7 @@
JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
jbyteArray buff = allocator->getStorageObjAndReset();
- return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
+ return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, NULL, -1);
}
static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index a1d41ee..d4c7600 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -345,14 +345,14 @@
///////////////////////////////////////////////////////////////////////////////////////////
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
- bool isMutable, jbyteArray ninepatch, int density)
+ bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+ int density)
{
SkASSERT(bitmap);
SkASSERT(bitmap->pixelRef());
-
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
- buffer, isMutable, ninepatch, density);
+ buffer, isMutable, ninepatch, layoutbounds, density);
hasException(env); // For the side effect of logging.
return obj;
}
@@ -360,7 +360,7 @@
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density)
{
- return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
+ return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
}
@@ -587,7 +587,7 @@
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
- "(I[BZ[BI)V");
+ "(I[BZ[B[II)V");
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cc32f44..c5b06f5 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -53,7 +53,8 @@
storage array (may be null).
*/
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
- bool isMutable, jbyteArray ninepatch, int density = -1);
+ bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+ int density = -1);
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density = -1);
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 365d985..df996af 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -31,14 +31,11 @@
// this relies on deserialization being done in place
Res_png_9patch::deserialize(patchNew);
patchNew->fileToDevice();
- if (fPatchIsValid) {
- free(fPatch);
- }
+ free(fPatch);
fPatch = patchNew;
//printf("9patch: (%d,%d)-(%d,%d)\n",
// fPatch.sizeLeft, fPatch.sizeTop,
// fPatch.sizeRight, fPatch.sizeBottom);
- fPatchIsValid = true;
// now update our host to force index or 32bit config
// 'cause we don't want 565 predithered, since as a 9patch, we know
@@ -52,8 +49,9 @@
SkBitmap::kARGB_8888_Config,
};
fHost->setPrefConfigTable(gNo565Pref);
- } else {
- fPatch = NULL;
+ } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) {
+ fLayoutBounds = new int[4];
+ memcpy(fLayoutBounds, data, sizeof(int) * 4);
}
return true; // keep on decoding
}
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 207536c..10d268a 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -28,17 +28,17 @@
NinePatchPeeker(SkImageDecoder* host) {
// the host lives longer than we do, so a raw ptr is safe
fHost = host;
- fPatchIsValid = false;
+ fPatch = NULL;
+ fLayoutBounds = NULL;
}
~NinePatchPeeker() {
- if (fPatchIsValid) {
- free(fPatch);
- }
+ free(fPatch);
+ delete fLayoutBounds;
}
- bool fPatchIsValid;
Res_png_9patch* fPatch;
+ int *fLayoutBounds;
virtual bool peek(const char tag[], const void* data, size_t length);
};
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
index 5b06460..93843fd 100644
--- a/core/res/res/layout/notification_template_base.xml
+++ b/core/res/res/layout/notification_template_base.xml
@@ -28,7 +28,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="fill_vertical"
android:layout_marginLeft="@dimen/notification_large_icon_width"
android:minHeight="@dimen/notification_large_icon_height"
android:orientation="vertical"
@@ -36,6 +36,7 @@
android:paddingRight="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
+ android:gravity="center_vertical"
>
<LinearLayout
android:id="@+id/line1"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 438c141..2b27585 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2079,6 +2079,7 @@
<!-- Locale -->
<enum name="locale" value="3" />
</attr>
+
<!-- Direction of the text. A heuristic is used to determine the resolved text direction
of paragraphs. -->
<attr name="textDirection" format="integer">
@@ -2099,6 +2100,29 @@
<!-- The paragraph direction is coming from the system Locale. -->
<enum name="locale" value="5" />
</attr>
+
+ <!-- Alignment of the text. A heuristic is used to determine the resolved
+ text alignment. -->
+ <attr name="textAlignment" format="integer">
+ <!-- Default -->
+ <enum name="inherit" value="0" />
+ <!-- Default for the root view. The gravity determines the alignment, ALIGN_NORMAL,
+ ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s
+ text direction -->
+ <enum name="gravity" value="1" />
+ <!-- Align to the start of the paragraph, e.g. ALIGN_NORMAL. -->
+ <enum name="textStart" value="2" />
+ <!-- Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. -->
+ <enum name="textEnd" value="3" />
+ <!-- Center the paragraph, e.g. ALIGN_CENTER. -->
+ <enum name="center" value="4" />
+ <!-- Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ layoutDirection is LTR, and ALIGN_RIGHT otherwise. -->
+ <enum name="viewStart" value="5" />
+ <!-- Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ layoutDirection is LTR, and ALIGN_LEFT otherwise -->
+ <enum name="viewEnd" value="6" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e5f049d..7341c6c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3554,6 +3554,7 @@
<public type="attr" name="supportsRtl" id="0x010103a8" />
<public type="attr" name="textDirection"/>
+ <public type="attr" name="textAlignment"/>
<public type="attr" name="layoutDirection" />
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index dc3ded1..b188538 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -42,9 +42,9 @@
<li class="value-1"><h4>Activity bar (shown with the Holo Dark theme)</h4>
<p>
-An indeterminate activity bar is used at the start of an application download because Google Play hasn't
-been able to contact the server yet, and it's not possible to determine how long it will take for
-the download to begin.
+An indeterminate activity bar is used at the start of an application download because the Play Store
+app hasn't been able to contact the server yet, and it's not possible to determine how long it will
+take for the download to begin.
</p>
</li>
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 2c854d3..19ed1c3 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -25,7 +25,7 @@
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions"> </div>
</div>
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 19b58d9..6dd8d61 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -35,6 +35,7 @@
<li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
</ul>
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 618c44b..67dfd79 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -39,7 +39,7 @@
<p>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe® Fireworks® PNG Stencil</a>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni® OmniGraffle® Stencil</a>
- <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe® Photoshop® Sources</a>
+ <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120302.zip">Adobe® Photoshop® Sources</a>
</p>
</div>
diff --git a/docs/html/design/get-started/principles.jd b/docs/html/design/get-started/principles.jd
index 8f5b446..0b7147b 100644
--- a/docs/html/design/get-started/principles.jd
+++ b/docs/html/design/get-started/principles.jd
@@ -10,7 +10,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Delight me in surprising ways</h4>
+<h4 id="delight-me">Delight me in surprising ways</h4>
<p>A beautiful surface, a carefully-placed animation, or a well-timed sound effect is a joy to
experience. Subtle effects contribute to a feeling of effortlessness and a sense that a powerful
force is at hand.</p>
@@ -28,7 +28,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Real objects are more fun than buttons and menus</h4>
+<h4 id="real-objects-more-fun">Real objects are more fun than buttons and menus</h4>
<p>Allow people to directly touch and manipulate objects in your app. It reduces the cognitive effort
needed to perform a task while making it more emotionally satisfying.</p>
@@ -45,7 +45,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Let me make it mine</h4>
+<h4 id="make-it-mine">Let me make it mine</h4>
<p>People love to add personal touches because it helps them feel at home and in control. Provide
sensible, beautiful defaults, but also consider fun, optional customizations that don't hinder
primary tasks.</p>
@@ -63,7 +63,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Get to know me</h4>
+<h4 id="get-to-know-me">Get to know me</h4>
<p>Learn peoples' preferences over time. Rather than asking them to make the same choices over and
over, place previous choices within easy reach.</p>
@@ -80,7 +80,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Keep it brief</h4>
+<h4 id="keep-it-brief">Keep it brief</h4>
<p>Use short phrases with simple words. People are likely to skip sentences if they're long.</p>
</div>
@@ -96,7 +96,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Pictures are faster than words</h4>
+<h4 id="pictures-faster-than-words">Pictures are faster than words</h4>
<p>Consider using pictures to explain ideas. They get people's attention and can be much more efficient
than words.</p>
@@ -113,7 +113,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Decide for me but let me have the final say</h4>
+<h4 id="decide-for-me">Decide for me but let me have the final say</h4>
<p>Take your best guess and act rather than asking first. Too many choices and decisions make people
unhappy. Just in case you get it wrong, allow for 'undo'.</p>
@@ -130,7 +130,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only show what I need when I need it</h4>
+<h4 id="only-show-when-i-need-it">Only show what I need when I need it</h4>
<p>People get overwhelmed when they see too much at once. Break tasks and information into small,
digestible chunks. Hide options that aren't essential at the moment, and teach people as they go.</p>
@@ -147,7 +147,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>I should always know where I am</h4>
+<h4 id="always-know-where-i-am">I should always know where I am</h4>
<p>Give people confidence that they know their way around. Make places in your app look distinct and
use transitions to show relationships among screens. Provide feedback on tasks in progress.</p>
@@ -164,7 +164,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Never lose my stuff</h4>
+<h4 id="never-lose-my-stuff">Never lose my stuff</h4>
<p>Save what people took time to create and let them access it from anywhere. Remember settings,
personal touches, and creations across phones, tablets, and computers. It makes upgrading the
easiest thing in the world.</p>
@@ -182,7 +182,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>If it looks the same, it should act the same</h4>
+<h4 id="looks-same-should-act-same">If it looks the same, it should act the same</h4>
<p>Help people discern functional differences by making them visually distinct rather than subtle.
Avoid modes, which are places that look similar but act differently on the same input.</p>
@@ -199,7 +199,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only interrupt me if it's important</h4>
+<h4 id="interrupt-only-if-important">Only interrupt me if it's important</h4>
<p>Like a good personal assistant, shield people from unimportant minutiae. People want to stay
focused, and unless it's critical and time-sensitive, an interruption can be taxing and frustrating.</p>
@@ -216,7 +216,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Give me tricks that work everywhere</h4>
+<h4 id="give-me-tricks">Give me tricks that work everywhere</h4>
<p>People feel great when they figure things out for themselves. Make your app easier to learn by
leveraging visual patterns and muscle memory from other Android apps. For example, the swipe gesture
may be a good navigational shortcut.</p>
@@ -234,7 +234,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>It's not my fault</h4>
+<h4 id="its-not-my-fault">It's not my fault</h4>
<p>Be gentle in how you prompt people to make corrections. They want to feel smart when they use your
app. If something goes wrong, give clear recovery instructions but spare them the technical details.
If you can fix it behind the scenes, even better.</p>
@@ -252,7 +252,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Sprinkle encouragement</h4>
+<h4 id="sprinkle-encouragement">Sprinkle encouragement</h4>
<p>Break complex tasks into smaller steps that can be easily accomplished. Give feedback on actions,
even if it's just a subtle glow.</p>
@@ -269,7 +269,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Do the heavy lifting for me</h4>
+<h4 id="do-heavy-lifting-for-me">Do the heavy lifting for me</h4>
<p>Make novices feel like experts by enabling them to do things they never thought they could. For
example, shortcuts that combine multiple photo effects can make amateur photographs look amazing in
only a few steps.</p>
@@ -287,7 +287,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Make important things fast</h4>
+<h4 id="make-important-things-fast">Make important things fast</h4>
<p>Not all actions are equal. Decide what's most important in your app and make it easy to find and
fast to use, like the shutter button in a camera, or the pause button in a music player.</p>
diff --git a/docs/html/design/media/app_structure_market.png b/docs/html/design/media/app_structure_market.png
index 3b0b786..5aa595e 100644
--- a/docs/html/design/media/app_structure_market.png
+++ b/docs/html/design/media/app_structure_market.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_music_lndscp.png b/docs/html/design/media/app_structure_music_lndscp.png
index 0dd400c..67354de 100644
--- a/docs/html/design/media/app_structure_music_lndscp.png
+++ b/docs/html/design/media/app_structure_music_lndscp.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_scrolltabs.png b/docs/html/design/media/app_structure_scrolltabs.png
index ef4fca4..ea742c2 100644
--- a/docs/html/design/media/app_structure_scrolltabs.png
+++ b/docs/html/design/media/app_structure_scrolltabs.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_shortcut_on_item.png b/docs/html/design/media/app_structure_shortcut_on_item.png
index 3874e1d4..1341f1f 100644
--- a/docs/html/design/media/app_structure_shortcut_on_item.png
+++ b/docs/html/design/media/app_structure_shortcut_on_item.png
Binary files differ
diff --git a/docs/html/design/media/iconography_launcher_example.png b/docs/html/design/media/iconography_launcher_example.png
index a5db53e..0cce7ef9 100644
--- a/docs/html/design/media/iconography_launcher_example.png
+++ b/docs/html/design/media/iconography_launcher_example.png
Binary files differ
diff --git a/docs/html/design/media/iconography_overview.png b/docs/html/design/media/iconography_overview.png
index 688c1b5..56cd409 100644
--- a/docs/html/design/media/iconography_overview.png
+++ b/docs/html/design/media/iconography_overview.png
Binary files differ
diff --git a/docs/html/design/media/migrating_intents.png b/docs/html/design/media/migrating_intents.png
new file mode 100644
index 0000000..65fc1a5
--- /dev/null
+++ b/docs/html/design/media/migrating_intents.png
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png b/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
deleted file mode 100644
index bab6aca..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
deleted file mode 100644
index bdccc2f..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
deleted file mode 100644
index 5e0135b..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_back.png b/docs/html/design/media/navigation_between_apps_back.png
new file mode 100755
index 0000000..ded5d0a
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_back.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_inward.png b/docs/html/design/media/navigation_between_apps_inward.png
new file mode 100755
index 0000000..1f5e401
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_inward.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_up.png b/docs/html/design/media/navigation_between_apps_up.png
new file mode 100755
index 0000000..f192c88
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_up.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market1.png b/docs/html/design/media/navigation_between_siblings_market1.png
old mode 100644
new mode 100755
index c3148f8..8f2b3dc
--- a/docs/html/design/media/navigation_between_siblings_market1.png
+++ b/docs/html/design/media/navigation_between_siblings_market1.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market2.png b/docs/html/design/media/navigation_between_siblings_market2.png
old mode 100644
new mode 100755
index 208be47..33b654c
--- a/docs/html/design/media/navigation_between_siblings_market2.png
+++ b/docs/html/design/media/navigation_between_siblings_market2.png
Binary files differ
diff --git a/docs/html/design/media/navigation_from_outside_up.png b/docs/html/design/media/navigation_from_outside_up.png
deleted file mode 100644
index eaa3cdb..0000000
--- a/docs/html/design/media/navigation_from_outside_up.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_indirect_notification.png b/docs/html/design/media/navigation_indirect_notification.png
new file mode 100644
index 0000000..6f99267
--- /dev/null
+++ b/docs/html/design/media/navigation_indirect_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_popup_notification.png b/docs/html/design/media/navigation_popup_notification.png
new file mode 100644
index 0000000..a0a3ee7
--- /dev/null
+++ b/docs/html/design/media/navigation_popup_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_up_vs_back_gmail.png b/docs/html/design/media/navigation_up_vs_back_gmail.png
index 71e6484..ff7adfe 100644
--- a/docs/html/design/media/navigation_up_vs_back_gmail.png
+++ b/docs/html/design/media/navigation_up_vs_back_gmail.png
Binary files differ
diff --git a/docs/html/design/media/navigation_with_back_and_up.png b/docs/html/design/media/navigation_with_back_and_up.png
index 4fb6dce..5440220 100644
--- a/docs/html/design/media/navigation_with_back_and_up.png
+++ b/docs/html/design/media/navigation_with_back_and_up.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity.png b/docs/html/design/media/progress_activity.png
index 51210b4..32cf1f5 100644
--- a/docs/html/design/media/progress_activity.png
+++ b/docs/html/design/media/progress_activity.png
Binary files differ
diff --git a/docs/html/design/media/progress_download.png b/docs/html/design/media/progress_download.png
index f567f74..ab6bf58 100644
--- a/docs/html/design/media/progress_download.png
+++ b/docs/html/design/media/progress_download.png
Binary files differ
diff --git a/docs/html/design/media/settings_checkbox.png b/docs/html/design/media/settings_checkbox.png
new file mode 100644
index 0000000..6615bfb
--- /dev/null
+++ b/docs/html/design/media/settings_checkbox.png
Binary files differ
diff --git a/docs/html/design/media/settings_date_time.png b/docs/html/design/media/settings_date_time.png
new file mode 100644
index 0000000..8df92d4
--- /dev/null
+++ b/docs/html/design/media/settings_date_time.png
Binary files differ
diff --git a/docs/html/design/media/settings_dependency.png b/docs/html/design/media/settings_dependency.png
new file mode 100644
index 0000000..4821c61
--- /dev/null
+++ b/docs/html/design/media/settings_dependency.png
Binary files differ
diff --git a/docs/html/design/media/settings_flowchart.png b/docs/html/design/media/settings_flowchart.png
new file mode 100644
index 0000000..7e8623c
--- /dev/null
+++ b/docs/html/design/media/settings_flowchart.png
Binary files differ
diff --git a/docs/html/design/media/settings_grouping.png b/docs/html/design/media/settings_grouping.png
new file mode 100644
index 0000000..d271ea8
--- /dev/null
+++ b/docs/html/design/media/settings_grouping.png
Binary files differ
diff --git a/docs/html/design/media/settings_individual_on_off.png b/docs/html/design/media/settings_individual_on_off.png
new file mode 100644
index 0000000..03bea0b
--- /dev/null
+++ b/docs/html/design/media/settings_individual_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_list_subscreen.png b/docs/html/design/media/settings_list_subscreen.png
new file mode 100644
index 0000000..385aa6a
--- /dev/null
+++ b/docs/html/design/media/settings_list_subscreen.png
Binary files differ
diff --git a/docs/html/design/media/settings_main.png b/docs/html/design/media/settings_main.png
new file mode 100644
index 0000000..f42a358
--- /dev/null
+++ b/docs/html/design/media/settings_main.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off.png b/docs/html/design/media/settings_master_on_off.png
new file mode 100644
index 0000000..e46bb97
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off_2.png b/docs/html/design/media/settings_master_on_off_2.png
new file mode 100644
index 0000000..ab4e992
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off_2.png
Binary files differ
diff --git a/docs/html/design/media/settings_multiple_choice.png b/docs/html/design/media/settings_multiple_choice.png
new file mode 100644
index 0000000..9b28566
--- /dev/null
+++ b/docs/html/design/media/settings_multiple_choice.png
Binary files differ
diff --git a/docs/html/design/media/settings_overflow.png b/docs/html/design/media/settings_overflow.png
new file mode 100644
index 0000000..9000bec
--- /dev/null
+++ b/docs/html/design/media/settings_overflow.png
Binary files differ
diff --git a/docs/html/design/media/settings_slider.png b/docs/html/design/media/settings_slider.png
new file mode 100644
index 0000000..51545c8
--- /dev/null
+++ b/docs/html/design/media/settings_slider.png
Binary files differ
diff --git a/docs/html/design/media/settings_subscreen_navigation.png b/docs/html/design/media/settings_subscreen_navigation.png
new file mode 100644
index 0000000..2ab0b96
--- /dev/null
+++ b/docs/html/design/media/settings_subscreen_navigation.png
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.mp4 b/docs/html/design/media/tabs_scrolly.mp4
index 4329243..dc9e9c6 100644
--- a/docs/html/design/media/tabs_scrolly.mp4
+++ b/docs/html/design/media/tabs_scrolly.mp4
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.ogv b/docs/html/design/media/tabs_scrolly.ogv
index 345e57a..3e484f9 100644
--- a/docs/html/design/media/tabs_scrolly.ogv
+++ b/docs/html/design/media/tabs_scrolly.ogv
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.webm b/docs/html/design/media/tabs_scrolly.webm
index 17e368e..e9d371d 100644
--- a/docs/html/design/media/tabs_scrolly.webm
+++ b/docs/html/design/media/tabs_scrolly.webm
Binary files differ
diff --git a/docs/html/design/media/ui_overview_all_apps.png b/docs/html/design/media/ui_overview_all_apps.png
index 467f5ad..17e7ece 100644
--- a/docs/html/design/media/ui_overview_all_apps.png
+++ b/docs/html/design/media/ui_overview_all_apps.png
Binary files differ
diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 9e3f48c..2226fec 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -176,7 +176,7 @@
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions"> </div>
</div>
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index b54b12f..e2398ed 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -7,7 +7,7 @@
single screen</li>
<li>Apps such as Phone whose main purpose is to switch between different activities without deeper
navigation</li>
-<li>Apps such as Gmail or Google Play that combine a broad set of data views with deep navigation</li>
+<li>Apps such as Gmail or the Play Store that combine a broad set of data views with deep navigation</li>
</ul>
<p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
<h2 id="general-structure">General Structure</h2>
@@ -60,7 +60,7 @@
<img src="{@docRoot}design/media/app_structure_market.png">
<div class="figure-caption">
- The Google Play app's start screen primarily allows navigation into the stores for Apps, Music, Books,
+ The Play Store app's start screen primarily allows navigation into the stores for Apps, Music, Books,
Movies and Games. It is also enriched with tailored recommendations and promotions that
surface content of interest to the user. Search is readily available from the action bar.
</div>
@@ -145,8 +145,8 @@
<img src="{@docRoot}design/media/app_structure_scrolltabs.png">
<div class="figure-caption">
- Google Play uses tabs to simultaneously show category choice and content. To navigate between
- categories, users can swipe left/right on the content.
+ The Play Store app uses tabs to simultaneously show category choice and content. To navigate
+ between categories, users can swipe left/right on the content.
</div>
</div>
diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd
index d35cd82..7e288ae 100644
--- a/docs/html/design/patterns/navigation.jd
+++ b/docs/html/design/patterns/navigation.jd
@@ -13,32 +13,36 @@
<h2 id="up-vs-back">Up vs. Back</h2>
-<p>The Up button is used to navigate within an application based on the hierarchical relationships
+<p>The Up button is used to navigate within an app based on the hierarchical relationships
between screens. For instance, if screen A displays a list of items, and selecting an item leads to
screen B (which presents that item in more detail), then screen B should offer an Up button that
returns to screen A.</p>
-<p>If a screen is the topmost one in an app (i.e. the home of the app), it should not present an Up
+<p>If a screen is the topmost one in an app (that is, the app's home), it should not present an Up
button.</p>
-<p>The system Back key is used to navigate based on the history of screens the user has recently seen,
-in reverse chronological order—in effect, the temporal relationships between screens.</p>
+
+<p>The system Back button is used to navigate, in reverse chronological order, through the history
+of screens the user has recently worked with. It is generally based on the temporal relationships
+between screens, rather than the app's hierarchy.</p>
+
<p>When the previously viewed screen is also the hierarchical parent of the current screen, pressing
-the Back key will have the same result as pressing an Up button -- this is a common occurrence.
-However, unlike the Up button, which ensures the user remains within your app, the Back key can
-return the user to the Home screen, or even to a different application.</p>
+the Back button has the same result as pressing an Up button—this is a common
+occurrence. However, unlike the Up button, which ensures the user remains within your app, the Back
+button can return the user to the Home screen, or even to a different app.</p>
<img src="{@docRoot}design/media/navigation_up_vs_back_gmail.png">
-<p>The Back key also supports a few behaviors not directly tied to screen-to-screen navigation:</p>
+<p>The Back button also supports a few behaviors not directly tied to screen-to-screen navigation:
+</p>
<ul>
-<li>Back dismisses floating windows (dialogs, popups)</li>
-<li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
-<li>Back hides the onscreen keyboard (IME)</li>
+<li>Dismisses floating windows (dialogs, popups)</li>
+<li>Dismisses contextual action bars, and removes the highlight from the selected items</li>
+<li>Hides the onscreen keyboard (IME)</li>
</ul>
<h2 id="within-app">Navigation Within Your App</h2>
<h4>Navigating to screens with multiple entry points</h4>
<p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
-from multiple entry points—e.g., a settings screen which can be navigated to from any screen
+from multiple entry points—such as a settings screen that can be reached from any other screen
in your app. In this case, the Up button should choose to return to the referring screen, behaving
identically to Back.</p>
<h4>Changing view within a screen</h4>
@@ -50,7 +54,7 @@
<li>Switching views using a dropdown (aka collapsed tabs)</li>
<li>Filtering a list</li>
<li>Sorting a list</li>
-<li>Changing display characteristics (e.g. zooming)</li>
+<li>Changing display characteristics (such as zooming)</li>
</ul>
<h4>Navigating between sibling screens</h4>
<p>When your app supports navigation from a list of items to a detail view of one of those items, it's
@@ -61,56 +65,140 @@
<img src="{@docRoot}design/media/navigation_between_siblings_gmail.png">
-<p>However, a notable exception to this occurs when browsing between "related" detail views not tied
-together by the referring list—for example, when browsing on Google Play between apps from
+<p>However, a notable exception to this occurs when browsing between related detail views not tied
+together by the referring list—for example, when browsing in the Play Store between apps from
the same developer, or albums by the same artist. In these cases, following each link does create
-history, causing the Back button to step through each screen of related content which has been
-viewed. Up should continue to bypass these related screens and navigate to the most recently viewed
-container screen.</p>
+history, causing the Back button to step through each previously viewed screen. Up should continue
+to bypass these related screens and navigate to the most recently viewed container screen.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market1.png">
<p>You have the ability to make the Up behavior even smarter based on your knowledge of detail
-view. If we extend our Google Play sample from above, imagine the user has navigated from the last Book
-viewed to the details for the Movie adaptation. In that case, Up can return to a container (Movies)
-which the user had not previously navigated through.</p>
+view. Extending the Play Store example from above, imagine the user has navigated from the last
+Book viewed to the details for the Movie adaptation. In that case, Up can return to a container
+(Movies) which the user hasn't previously navigated through.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market2.png">
-<h2 id="from-outside">Navigation From Outside Your App</h2>
+<h2 id="into-your-app">Navigation into Your App via Home Screen Widgets and Notifications</h2>
-<p>There are two categories of navigation from outside your app to screens deep within the app's
-hierarchy:</p>
+<p>You can use Home screen widgets or notifications to help your users navigate directly to screens
+deep within your app's hierarchy. For example, Gmail's Inbox widget and new message notification can
+both bypass the Inbox screen, taking the user directly to a conversation view.</p>
+
+<p>For both of these cases, handle the Up button as follows:</p>
+
<ul>
-<li>App-to-app navigation, such as via intent completion.</li>
-<li>System-to-app navigation, such as via notifications and home screen widgets.</li>
+<li><em>If the destination screen is typically reached from one particular screen within your
+app</em>, Up should navigate to that screen.</li>
+<li><em>Otherwise</em>, Up should navigate to the topmost ("Home") screen of your app.</li>
</ul>
-<p>Gmail provides examples of each of these. For app-to-app navigation, a "Share" intent goes directly
-to the compose screen. For system-to-app navigation, both a new message notification and a home
-screen widget can bypass the Inbox screen, taking the user directly to a conversation view.</p>
-<h4>App-to-app navigation</h4>
-<p>When navigating deep into your app's hierarchy directly from another app via an intent, Back will
-return to the referring app.</p>
-<p>The Up button is handled as follows:
-- If the destination screen is typically reached from one particular screen within your app, Up
- should navigate to that screen.
-- Otherwise, Up should navigate to the topmost ("Home") screen of your app.</p>
-<p>For example, after choosing to share a book being viewed on Google Play, the user navigates directly to
-Gmail's compose screen. From there, Up returns to the Inbox (which happens to be both the
-typical referrer to compose, as well as the topmost screen of the app), while Back returns to
-Google Play.</p>
-<img src="{@docRoot}design/media/navigation_from_outside_up.png">
+<p>In the case of the Back button, you should make navigation more predictable by inserting into the
+task's back stack the complete upward navigation path to the app's topmost screen. This allows users
+who've forgotten how they entered your app to navigate to the app's topmost screen before
+exiting.</p>
-<h4>System-to-app navigation</h4>
-<p>If your app was reached via the system mechanisms of notifications or home screen widgets, Up
-behaves as described for app-to-app navigation, above.</p>
-<p>For the Back key, you should make navigation more predictably by inserting into the task's back
-stack the complete upward navigation path to the app's topmost screen. This way, a user who has
-forgotten how they entered your app can safely navigate to the app's topmost screen before exiting
-it.</p>
-<p>For example, Gmail's Home screen widget has a button for diving directly to its compose screen.
-After following that path, the Back key first returns to the Inbox, and from there continues to
-Home.</p>
+<p>As an example, Gmail's Home screen widget has a button for diving directly to its compose
+screen. Up or Back from the compose screen would take the user to the Inbox, and from there the
+Back button continues to Home.</p>
<img src="{@docRoot}design/media/navigation_from_outside_back.png">
+
+<h4>Indirect notifications</h4>
+
+<p>When your app needs to present information about multiple events simultaneously, it can use a
+single notification that directs the user to an interstitial screen. This screen summarizes these
+events, and provides paths for the user to dive deeply into the app. Notifications of this style are
+called <em>indirect notifications</em>.</p>
+
+<p>Unlike standard (direct) notifications, pressing Back from an indirect notification's
+interstitial screen returns the user to the point the notification was triggered from—no
+additional screens are inserted into the back stack. Once the user proceeds into the app from its
+interstitial screen, Up and Back behave as for standard notifications, as described above:
+navigating within the app rather than returning to the interstitial.</p>
+
+<p>For example, suppose a user in Gmail receives an indirect notification from Calendar. Touching
+this notification opens the interstitial screen, which displays reminders for several different
+events. Touching Back from the interstitial returns the user to Gmail. Touching on a particular
+event takes the user away from the interstitial and into the full Calendar app to display details of
+the event. From the event details, Up and Back navigate to the top-level view of Calendar.</p>
+
+<img src="{@docRoot}design/media/navigation_indirect_notification.png">
+
+<h4>Pop-up notifications</h4>
+
+<p><em>Pop-up notifications</em> bypass the notification drawer, instead appearing directly in
+front of the user. They are rarely used, and <strong>should be reserved for occasions where a timely
+response is required and the interruption of the user's context is necessary</strong>. For example,
+Talk uses this style to alert the user of an invitation from a friend to join a video chat, as this
+invitation will automatically expire after a few seconds.</p>
+
+<p>In terms of navigation behavior, pop-up notifications closely follow the behavior of an indirect
+notification's interstitial screen. Back dismisses the pop-up notification. If the user navigates
+from the pop-up into the notifying app, Up and Back follow the rules for standard notifications,
+navigating within the app.</p>
+
+<img src="{@docRoot}design/media/navigation_popup_notification.png">
+
+<h2 id="between-apps">Navigation Between Apps</h2>
+
+<p>One of the fundamental strengths of the Android system is the ability for apps to activate each
+other, giving the user the ability to navigate directly from one app into another. For example, an
+app that needs to capture a photo can activate the Camera app, which will return the photo
+to the referring app. This is a tremendous benefit to both the developer, who can easily leverage
+code from other apps, and the user, who enjoys a consistent experience for commonly performed
+actions.</p>
+
+<p>To understand app-to-app navigation, it's important to understand the Android framework behavior
+discussed below.</p>
+
+<h4>Activities, tasks, and intents</h4>
+
+<p>In Android, an <strong>activity</strong> is an application component that defines a screen of
+information and all of the associated actions the user can perform. Your app is a collection of
+activities, consisting of both the activities you create and those you re-use from other apps.</p>
+
+<p>A <strong>task</strong> is the sequence of activities a user follows to accomplish a goal. A
+single task can make use of activities from just one app, or may draw on activities from a number
+of different apps.</p>
+
+<p>An <strong>intent</strong> is a mechanism for one app to signal it would like another
+app's assistance in performing an action. An app's activities can indicate which intents
+they can respond to. For common intents such as "Share", the user may have many apps installed
+that can fulfill that request.</p>
+
+<h4>Example: navigating between apps to support sharing</h4>
+
+<p>To understand how activities, tasks, and intents work together, consider how one app allows users
+to share content by using another app. For example, launching the Play Store app from Home begins
+new Task A (see figure below). After navigating through the Play Store and touching a promoted book
+to see its details, the user remains in the same task, extending it by adding activities. Triggering
+the Share action prompts the user with a dialog listing each of the activities (from different apps)
+which have registered to handle the Share intent.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_inward.png">
+
+<p>When the user elects to share via Gmail, Gmail's compose activity is added as a continuation of
+Task A—no new task is created. If Gmail had its own task running in the background, it would
+be unaffected.</p>
+
+<p>From the compose activity, sending the message or touching the Back button returns the user to
+the book details activity. Subsequent touches of Back continue to navigate back through the Play
+Store, ultimately arriving at Home.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_back.png">
+
+<p>However, by touching Up from the compose activity, the user indicates a desire to remain within
+Gmail. Gmail's conversation list activity appears, and a new Task B is created for it. New tasks are
+always rooted to Home, so touching Back from the conversation list returns there.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_up.png">
+
+<p>Task A persists in the background, and the user may return to it later (for example, via the
+Recents screen). If Gmail already had its own task running in the background, it would be replaced
+with Task B—the prior context is abandoned in favor of the user's new goal.</p>
+
+<p>When your app registers to handle intents with an activity deep within the app's hierarchy,
+refer to <a href="#into-your-app">Navigation into Your App via Home Screen Widgets and
+Notifications</a> for guidance on how to specify Up navigation.</p>
diff --git a/docs/html/design/patterns/pure-android.jd b/docs/html/design/patterns/pure-android.jd
index 8ed1347..77813c0 100644
--- a/docs/html/design/patterns/pure-android.jd
+++ b/docs/html/design/patterns/pure-android.jd
@@ -48,7 +48,8 @@
document or deleting.</p>
<p>As you are migrating your app to Android, please swap out platform-specific icons with their Android
counterparts.</p>
-<p>You can find a wide variety of icons for use in your app in the Android SDK.</p>
+<p>You can find a wide variety of icons for use in your app on the
+<a href="{@docRoot}design/downloads/index.html">Downloads</a> page.</p>
</div>
<div class="layout-content-col span-8">
@@ -89,6 +90,33 @@
<div class="layout-content-row">
<div class="layout-content-col span-5">
+<h4>Don't hardcode links to other apps</h4>
+<p>In some cases you might want your app to take advantage of another app's feature set. For
+example, you may want to share the content that your app created via a social network or messaging
+app, or view the content of a weblink in a browser. Don't use hard-coded, explicit links to
+particular apps to achieve this. Instead, use Android's intent API to launch an activity chooser
+which lists all applications that are set up to handle the particular request. This lets the user
+complete the task with their preferred app. For sharing in particular, consider using the <em>Share
+Action Provider</em> in your action bar to provide faster access to the user's most recently used
+sharing target.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/migrating_intents.png">
+ <div class="figure-caption">
+ Link to other apps with the activity chooser or use the <em>Share Action Provider</em> in the
+ action bar.
+ </div>
+
+ </div>
+</div>
+
+<div class="vspace size-2"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5">
+
<h4>Don't use labeled back buttons on action bars</h4>
<p>Other platforms use an explicit back button with label to allow the user to navigate up the
application's hierarchy. Instead, Android uses the main action bar's app icon for hierarchical
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
new file mode 100644
index 0000000..3b28b84
--- /dev/null
+++ b/docs/html/design/patterns/settings.jd
@@ -0,0 +1,689 @@
+page.title=Settings
+@jd:body
+
+<p>Settings is a place in your app where users indicate their preferences for how your app should
+behave. This benefits users because:</p>
+
+<ul>
+<li>You don't need to interrupt them with the same questions over and over when certain situations
+arise. The settings predetermine what will always happen in those situations (see design
+principle: <a href="{@docRoot}design/get-started/principles.html#decide-for-me">Decide for me but
+let me have the final say</a>).</li>
+<li>You help them feel at home and in control (see design principle:
+<a href="{@docRoot}design/get-started/principles.html#make-it-mine">Let me make it mine</a>).</li>
+</ul>
+
+<h2 id="flow-structure">Flow and Structure</h2>
+
+<h4 id="settings-access">Provide access to Settings in the action overflow</h4>
+
+<p>Settings is given low prominence in the UI because it's not frequently needed. Even if there's
+room in the <a href="{@docRoot}design/patterns/actionbar.html">action bar</a>, never make Settings
+an action button. Always keep it in the action overflow and label it "Settings". Place it below
+all other items except "Help".</p>
+
+<img src="{@docRoot}design/media/settings_overflow.png">
+
+<div class="vspace size-2"> </div>
+
+<h4 id="what-to-make-a-setting">Avoid the temptation to make everything a setting</h4>
+
+<p>Because Settings is a few navigational steps away, no matter how many items you have, they'll
+never clutter up the core part of your UI. This may seem like good news, but it also poses a
+challenge.</p>
+
+<p>Settings can be a tempting place to keep a lot of stuff—like a hall closet where things
+get stashed when you tidy up before company comes over. It's not a place where you spend lots of
+time, so it's easy to rationalize and ignore its cluttered condition. But when users visit
+Settings—however infrequently—they'll have the same expectations for the experience as
+they do everywhere else in your app. More settings means more choices to make, and too many are
+overwhelming.</p>
+
+<p>So don't punt on the difficult product decisions and debates that can bring on the urge to
+"just make it a setting". For each control you're considering adding to Settings, make sure it
+meets the bar:</p>
+
+<img src="{@docRoot}design/media/settings_flowchart.png">
+
+<div class="vspace size-3"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5 with-callouts">
+
+<h4 id="group-settings">If you still have lots of settings, group related settings together</h4>
+
+<p>The number of items an average human can hold in short-term memory is 7±2. If you
+present a list of 10 or more settings (even after applying the criteria above), users will have
+more difficulty scanning, comprehending, and processing them.</p>
+
+<p>You can remedy this by dividing some or all of the settings into groups, effectively turning
+one long list into multiple shorter lists. A group of related settings can be presented in one of
+two ways:</p>
+
+<ol>
+<li><h4>Under a section divider</h4></li>
+<li><h4>In a separate subscreen</h4></li>
+</ol>
+
+<p>You can use one or both these grouping techniques to organize your app's settings.</p>
+
+<p>For example, in the main screen of the Android Settings app, each item in the list navigates
+to a subscreen of related settings. In addition, the items themselves are grouped under section
+dividers.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/settings_grouping.png">
+
+ </div>
+</div>
+
+<p>Grouping settings is not an exact science, but here's some advice for how to approach it, based
+on the total number of settings in your app.</p>
+
+<div class="vspace size-1"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>7 or fewer</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Don't group them at all. It won't benefit users and will seem like overkill.</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>8 to 10</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Try grouping related settings under 1 or 2 section dividers. If you have any "singletons"
+(settings that don't relate to any other settings and can't be grouped under your section
+dividers), treat them as follows:</p>
+
+<ul>
+<li>If they include some of your most important settings, list them at the top without a section
+divider.</li>
+<li>Otherwise, list them at the bottom with a section divider called "OTHER", in order of
+importance.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>11 to 15</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Same advice as above, but try 2 to 4 section dividers.</p>
+
+<p>Also, try the following to reduce the list:</p>
+
+<ul>
+<li>If 2 or more of the settings are mainly for power users, move them out of your main Settings
+screen and into an "Advanced" subscreen. Place an item in the action overflow called "Advanced" to
+navigate to it.</li>
+<li>Look for "doubles": two settings that relate to one another, but not to any other settings.
+Try to combine them into one setting, using the design patterns described later in this section.
+For example, you might be able to redesign two related checkbox settings into one multiple choice
+setting.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>16 or more</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>If you have any instances of 4 or more related settings, group them under a subscreen. Then use
+the advice suggested above for the reduced list size.</p>
+
+ </div>
+</div>
+
+
+<h2 id="patterns">Design Patterns</h2>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Checkbox</h4>
+<p>Use this pattern for a setting that is either selected or not selected.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_checkbox.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Multiple choice</h4>
+<p>Use this pattern for a setting that needs to present a discrete set of options, from which the
+user can choose only one.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_multiple_choice.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Slider</h4>
+<p>Use this pattern for a setting where the range of values are not discrete and fall along a
+continuum.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_slider.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Date/time</h4>
+<p>Use this pattern for a setting that needs to collect a date and/or time from the user.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_date_time.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Subscreen navigation</h4>
+<p>Use this pattern for navigating to a subscreen or sequence of subscreens that guide the user
+through a more complex setup process.</p>
+<ul>
+<li>If navigating to a single subscreen, use the same title in both the subscreen and the label
+navigating to it.</li>
+<li>If navigating to a sequence of subscreens (as in this example), use a title that describes the
+first step in the sequence.</li>
+</ul>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_subscreen_navigation.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>List subscreen</h4>
+<p>Use this pattern for a setting or category of settings that contains a list of equivalent items.
+</p>
+<p>The label provides the name of the item, and secondary text may be used for status. (In this
+example, status is reinforced with an icon to the right of the label.) Any actions associated with
+the list appear in the action bar rather than the list itself.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_list_subscreen.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Master on/off switch</h4>
+<p>Use this pattern for a category of settings that need a mechanism for turning on or off as a
+whole.</p>
+<p>An on/off switch is placed as the first item in the action bar of a subscreen. When the switch
+is turned off, the items in the list disappear, replaced by text that describes why the list is
+empty. If any actions require the switch to be on, they become disabled.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<div class="vspace size-2"> </div>
+
+<p>You can also echo the master on/off switch in the menu item that leads to the subscreen.
+However, you should only do this in cases where users rarely need to access the subscreen once
+it's initially set up and more often just want to toggle the switch.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off_2.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Individual on/off switch</h4>
+<p>Use this pattern for an individual setting that requires a more elaborate description than can
+be provided in checkbox form.</p>
+<p>The on/off switch only appears in the subscreen so that users aren't able to toggle it without
+also being exposed to the descriptive text. Secondary text appears below the setting label to
+reflect the current selection.</p>
+<p>In this example, Android Beam is on by default. Since users might not know what this setting
+does, we made the status more descriptive than just "On".</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_individual_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Dependency</h4>
+<p>Use this pattern for a setting that changes availability based on the value of another setting.
+</p>
+<p>The disabled setting appears below its dependency, without any indentation. If the setting
+includes a status line, it says "Unavailable", and if the reason isn't obvious, a brief
+explanation is included in the status.</p>
+<p>If a given setting is a dependency to 3 or more settings, consider using a subscreen with a
+master on/off switch so that your main settings screen isn't cluttered by lots of disabled items.
+</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_dependency.png">
+
+ </div>
+</div>
+
+<h2 id="defaults">Defaults</h2>
+
+<p>Take great care in choosing default values for each of your settings. Because settings
+determine app behavior, your choices will contribute to users' first impressions of your app. Even
+though users can change settings, they'll expect the initial states to be sensible. The following
+questions (when applicable) may help inform your decisions:</p>
+
+<ul>
+<li>Which choice would most users be likely to choose on their own if there were no default?</li>
+<li>Which choice is the most neutral or middle-of-the-road?</li>
+<li>Which choice is the least risky, controversial, or over-the-top?</li>
+<li>Which choice uses the least amount of battery or mobile data?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#never-lose-my-stuff">Never lose my stuff</a>?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#interrupt-only-if-important">Only interrupt
+me if it's important</a>?
+</li>
+</ul>
+
+<h2 id="writing">Writing Guidelines</h2>
+
+<h4>Label clearly and concisely</h4>
+
+<p>Writing a good label for a setting can be challenging because space is very limited. You only
+get one line, and it's incredibly short on the smallest of devices. Follow these guidelines to
+make your labels brief, meaningful, and scannable:</p>
+
+<ul>
+<li>Write each label in sentence case (i.e. only the first word and proper nouns are capitalized).
+</li>
+<li>Don't start a label with an instructional verb like "Set", "Change", "Edit", "Modify",
+"Manage", "Use", "Select", or "Choose". Users already understand that they can do these things to
+settings.</li>
+<li>Likewise, don't end a label with a word like "setting" or "settings". It's already implied.
+</li>
+<li>If the setting is part of a grouping, don't repeat the word(s) used in the section divider or
+subscreen title.</li>
+<li>Avoid starting a label with a negative word like "Don't" or "Never". For example, "Don't
+allow" could be rephrased to "Block".</li>
+<li>Steer clear of technical jargon as much as possible, unless it's a term widely understood by
+your target users. Use common verbs and nouns to convey the setting's purpose rather than its
+underlying technology.</li>
+<li>Don't refer to the user. For example, for a setting allowing the user to turn notifications on
+or off, label it "Notifications" instead of "Notify me".</li>
+</ul>
+
+<p>Once you've decided on labels for your settings, be sure to preview them on an
+<a href="{@docRoot}design/style/metrics-grids.html">LDPI handset</a> in portrait to make sure
+they'll fit everywhere.</p>
+
+<h4>Secondary text below is for status, not description…</h4>
+
+<p>Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe
+it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status.
+</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+</div>
+
+<p>Status in secondary text has the following benefits:</p>
+<ul>
+<li>Users can see at a glance what the current value of a setting is without having to navigate
+any further.</li>
+<li>It applies the design principle
+<a href="{@docRoot}design/get-started/principles.html#keep-it-brief">Keep it brief</a>, which
+users greatly appreciate.</li>
+</ul>
+
+<h4>…unless it's a checkbox setting</h4>
+<p>There's one important exception to the using secondary text for status: checkbox settings.
+Here, use secondary text for description, not status. Status below a checkbox is unnecessary
+because the checkbox already indicates it. The reason why it's appropriate to have a description
+below a checkbox setting is because—unlike other controls—it doesn't display a dialog
+or navigate to another screen where additional information can be provided.</p>
+
+<p>That said, if a checkbox setting's label is clear enough on its own, there's no need to also
+provide a description. Only include one if necessary.</p>
+
+<p>Follow these guidelines to write checkbox setting descriptions:</p>
+<ul>
+<li>Keep it to one sentence and don't use ending punctuation.</li>
+<li>Convey what happens when the setting is checked, phrased in the form of a command. Example:
+"Allow data exchange", not "Allows data exchange".</li>
+<li>Avoid repetition by choosing words that don't already appear in the label.</li>
+<li>Don't refer to the user unless it's necessary for understanding the setting.</li>
+<li>If you must refer to the user, do so in the second person ("you") rather than the first person
+("I"). Android speaks to users, not on behalf of them.</li>
+</ul>
+
+<h4>Writing examples</h4>
+
+<p>The following are examples of changes we made to labels and secondary text in the Settings app
+in Ice Cream Sandwich.</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Use tactile feedback
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Vibrate on touch
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting, we eliminated the throwaway word "Use" and rephrased the label to be
+more direct and understandable.</p>
+
+ </div>
+
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this multiple choice setting, we changed the label to a friendlier term and also replaced
+the description with status. We put some descriptive words around the selected value, "10
+minutes", because on its own, the meaning could be misinterpreted as "sleep for 10 minutes".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Change screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Change or disable pattern, PIN, or password security
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Pattern
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>This setting navigates to a a sequence of subscreens that allow users to choose a type of
+screen lock and then set it up. We eliminated the throwaway word "Change" in the label, and
+replaced the description with the current type of screen lock set up by the user. If the user
+hasn't set up a screen lock, the secondary text says "None".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Use Near Field Communication to read and exchange tags
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Allow data exchange when the phone touches another device
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting—although it's technical jargon—we kept the "NFC" label
+because: (1) we couldn't find a clear, concise alternative, and (2) user familiarity with the
+acronym is expected to increase dramatically in the next couple of years.</p>
+<p>We did, however, rewrite the description. It's far less technical than before and does a better
+job of conveying how and why you'd use NFC. We didn't include what the acronym stands for because
+it doesn't mean anything to most users and would have taken up a lot of space.</p>
+
+ </div>
+</div>
+
+<h2 id="checklist">Checklist</h2>
+<ul>
+<li><p>Make sure each item in Settings meets the criteria for belonging there.</p></li>
+<li><p>If you have more than 7 items, explore ways to group related settings.</p></li>
+<li><p>Use design patterns wherever applicable so users don't face a learning curve.</p></li>
+<li><p>Choose defaults that are safe, neutral, and fit the majority of users.</p></li>
+<li><p>Give each setting a clear, concise label and use secondary text appropriately.</p></li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
index 80fd03e..919ea7aa 100644
--- a/docs/html/design/style/writing.jd
+++ b/docs/html/design/style/writing.jd
@@ -1,58 +1,6 @@
page.title=Writing Style
@jd:body
-<style>
-
-/* UI tables */
-
-.ui_table {
- width: 100%;
- background: #282828;
- color: #fff;
- border-radius: 2px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.25);
- border-collapse: separate;
-}
-
-.ui_table th,
-.ui_table td {
- padding: 5px 10px;
-}
-
-.ui_table thead th {
- font-weight: 600;
-}
-
-.ui_table tfoot td {
- border-top: 1px solid #494949;
- border-right: 1px solid #494949;
- text-align: center;
-}
-
-.ui_table tfoot td:last-child {
- border-right: 0;
-}
-
-.list_item_margins {
- margin-left: 30px !important;
-}
-
-.example_label {
- margin-bottom: 10px;
- padding-left: 20px;
- background: transparent none no-repeat scroll 0px 3px;
-}
-
-.example_label.bad {
- background-image: url({@docRoot}assets/design/ico_wrong.png);
-}
-
-.example_label.good {
- background-image: url({@docRoot}assets/design/ico_good.png);
-}
-
-</style>
-
<p>When choosing words for your app:</p>
<ol>
<li>
@@ -90,20 +38,20 @@
<ol><li class="value-1"><strong>Keep it brief.</strong> From the setup wizard:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Too formal</div>
+ <div class="do-dont-label bad">Too formal</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Consult the documentation that came with your phone for further instructions.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Read the instructions that came with your phone.
</td></tr></tbody></table>
@@ -115,11 +63,11 @@
<ol><li class="value-2"><strong>Keep it simple.</strong> From the Location settings screen:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing</div>
+ <div class="do-dont-label bad">Confusing</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -139,9 +87,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
@@ -167,12 +115,12 @@
crashes:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing and annoying—"Sorry" just rubs salt in the
+ <div class="do-dont-label bad">Confusing and annoying—"Sorry" just rubs salt in the
wound.</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th colspan="3">
@@ -200,9 +148,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
+ <div class="do-dont-label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th colspan="3">
@@ -234,20 +182,20 @@
<ol><li class="value-4"><strong>Put the most important thing first.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Top news last</div>
+ <div class="do-dont-label bad">Top news last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
77 other people +1'd this, including Larry Page.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Top news first</div>
+ <div class="do-dont-label good">Top news first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Larry Page and 77 others +1'd this.
</td></tr></tbody></table>
@@ -255,20 +203,20 @@
</div>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Task last</div>
+ <div class="do-dont-label bad">Task last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
Touch Next to complete setup using a Wi-Fi connection.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Task first</div>
+ <div class="do-dont-label good">Task first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
To finish setup using Wi-Fi, touch Next.
</td></tr></tbody></table>
@@ -280,11 +228,11 @@
<ol><li class="value-5"><strong>Describe only what's necessary, and no more.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">From a Setup Wizard screen</div>
+ <div class="do-dont-label bad">From a Setup Wizard screen</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -306,9 +254,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">From a Setup Wizard screen</div>
+ <div class="do-dont-label good">From a Setup Wizard screen</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
diff --git a/docs/html/shareables/training/LocationAware.zip b/docs/html/shareables/training/LocationAware.zip
index e1926fa..46970cd 100644
--- a/docs/html/shareables/training/LocationAware.zip
+++ b/docs/html/shareables/training/LocationAware.zip
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6f939be..ed5b2f6 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -16,9 +16,12 @@
package android.graphics;
+import android.os.Debug;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.Log;
+
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@@ -57,6 +60,7 @@
private final boolean mIsMutable;
private byte[] mNinePatchChunk; // may be null
+ private int[] mLayoutBounds; // may be null
private int mWidth = -1;
private int mHeight = -1;
private boolean mRecycled;
@@ -95,6 +99,19 @@
*/
/*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
int density) {
+ this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
+ }
+
+ /**
+ * @noinspection UnusedDeclaration
+ */
+ /* Private constructor that must received an already allocated native
+ bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
+ int[] layoutBounds, int density) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
}
@@ -106,6 +123,7 @@
mIsMutable = isMutable;
mNinePatchChunk = ninePatchChunk;
+ mLayoutBounds = layoutBounds;
if (density >= 0) {
mDensity = density;
}
@@ -164,6 +182,16 @@
}
/**
+ * Sets the layout bounds as an array of left, top, right, bottom integers
+ * @param padding the array containing the padding values
+ *
+ * @hide
+ */
+ public void setLayoutBounds(int[] bounds) {
+ mLayoutBounds = bounds;
+ }
+
+ /**
* Free the native object associated with this bitmap, and clear the
* reference to the pixel data. This will not free the pixel data synchronously;
* it simply allows it to be garbage collected if there are no other references.
@@ -690,6 +718,14 @@
}
/**
+ * @hide
+ * @return the layout padding [left, right, top, bottom]
+ */
+ public int[] getLayoutBounds() {
+ return mLayoutBounds;
+ }
+
+ /**
* Specifies the known formats a bitmap can be compressed into
*/
public enum CompressFormat {
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c5705f6..1599e40 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -424,6 +424,7 @@
throw new ArrayIndexOutOfBoundsException();
}
Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
+
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
@@ -554,7 +555,6 @@
if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
return bm;
}
-
byte[] np = bm.getNinePatchChunk();
final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
if (opts.inScaled || isNinePatch) {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 043adae..86e824b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -773,7 +773,13 @@
np = null;
pad = null;
}
- return drawableFromBitmap(res, bm, np, pad, srcName);
+ int[] layoutBounds = bm.getLayoutBounds();
+ Rect layoutBoundsRect = null;
+ if (layoutBounds != null) {
+ layoutBoundsRect = new Rect(layoutBounds[0], layoutBounds[1],
+ layoutBounds[2], layoutBounds[3]);
+ }
+ return drawableFromBitmap(res, bm, np, pad, layoutBoundsRect, srcName);
}
return null;
}
@@ -875,7 +881,7 @@
Bitmap bm = BitmapFactory.decodeFile(pathName);
if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, pathName);
+ return drawableFromBitmap(null, bm, null, null, null, pathName);
}
return null;
@@ -956,10 +962,12 @@
}
private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
- Rect pad, String srcName) {
+ Rect pad, Rect layoutBounds, String srcName) {
if (np != null) {
- return new NinePatchDrawable(res, bm, np, pad, srcName);
+ NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName);
+ npd.setLayoutBounds(layoutBounds);
+ return npd;
}
return new BitmapDrawable(res, bm);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 18b8bc7..1272071 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -47,6 +47,7 @@
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
+ private Rect mLayoutBounds;
private Paint mPaint;
private boolean mMutated;
@@ -98,6 +99,13 @@
mNinePatchState.mTargetDensity = mTargetDensity;
}
+ /**
+ * @hide
+ */
+ void setLayoutBounds(Rect layoutBounds) {
+ mLayoutBounds = layoutBounds;
+ }
+
private void setNinePatchState(NinePatchState state, Resources res) {
mNinePatchState = state;
mNinePatch = state.mNinePatch;
@@ -258,7 +266,7 @@
}
options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
- final Rect padding = new Rect();
+ final Rect padding = new Rect();
Bitmap bitmap = null;
try {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 216c451..39d2e39 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1328,18 +1328,18 @@
* are set up for each individual segment.
*/
void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
- GLvoid* lengthCoords, float boundaryWidthProportion) {
+ GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
bool force = mCaches.unbindMeshBuffer();
mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
vertices, gAAVertexStride);
mCaches.resetTexCoordsVertexPointer();
mCaches.unbindIndicesBuffer();
- int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+ widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
glEnableVertexAttribArray(widthSlot);
glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
- int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+ lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
glEnableVertexAttribArray(lengthSlot);
glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
@@ -1348,7 +1348,12 @@
// Setting the inverse value saves computations per-fragment in the shader
int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
- glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+ glUniform1f(inverseBoundaryWidthSlot, 1.0f / boundaryWidthProportion);
+}
+
+void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
+ glDisableVertexAttribArray(widthSlot);
+ glDisableVertexAttribArray(lengthSlot);
}
void OpenGLRenderer::finishDrawTexture() {
@@ -1720,13 +1725,18 @@
float width = right - left;
float height = bottom - top;
+ int widthSlot;
+ int lengthSlot;
+
float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+ boundaryWidthProportion, widthSlot, lengthSlot);
+
int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion));
+ glUniform1f(inverseBoundaryLengthSlot, (1.0f / boundaryHeightProportion));
if (!quickReject(left, top, right, bottom)) {
AAVertex::set(aaVertices++, left, bottom, 1, 1);
@@ -1736,6 +1746,8 @@
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
+
+ finishDrawAALine(widthSlot, lengthSlot);
}
/**
@@ -1766,11 +1778,14 @@
// A stroke width of 0 has a special meaning in Skia:
// it draws a line 1 px wide regardless of current transform
bool isHairLine = paint->getStrokeWidth() == 0.0f;
+
float inverseScaleX = 1.0f;
float inverseScaleY = 1.0f;
bool scaled = false;
+
int alpha;
SkXfermode::Mode mode;
+
int generatedVerticesCount = 0;
int verticesCount = count;
if (count > 4) {
@@ -1790,10 +1805,13 @@
float m10 = mat->data[Matrix4::kSkewX];
float m11 = mat->data[Matrix4::kScaleX];
float m12 = mat->data[6];
+
float scaleX = sqrtf(m00 * m00 + m01 * m01);
float scaleY = sqrtf(m10 * m10 + m11 * m11);
+
inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+
if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
scaled = true;
}
@@ -1823,10 +1841,16 @@
// Expand boundary to enable AA calculations on the quad border
halfStrokeWidth += .5f;
}
+
+ int widthSlot;
+ int lengthSlot;
+
Vertex lines[verticesCount];
Vertex* vertices = &lines[0];
+
AAVertex wLines[verticesCount];
AAVertex* aaVertices = &wLines[0];
+
if (CC_UNLIKELY(!isAA)) {
setupDrawVertices(vertices);
} else {
@@ -1838,7 +1862,8 @@
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+ boundaryWidthProportion, widthSlot, lengthSlot);
}
AAVertex* prevAAVertex = NULL;
@@ -1848,10 +1873,12 @@
int inverseBoundaryLengthSlot = -1;
int boundaryWidthSlot = -1;
int inverseBoundaryWidthSlot = -1;
+
for (int i = 0; i < count; i += 4) {
// a = start point, b = end point
vec2 a(points[i], points[i + 1]);
vec2 b(points[i + 2], points[i + 3]);
+
float length = 0;
float boundaryLengthProportion = 0;
float boundaryWidthProportion = 0;
@@ -1868,6 +1895,7 @@
}
n *= wideningFactor;
}
+
if (scaled) {
n.x *= inverseScaleX;
n.y *= inverseScaleY;
@@ -1878,11 +1906,13 @@
extendedN /= 2;
extendedN.x *= inverseScaleX;
extendedN.y *= inverseScaleY;
+
float extendedNLength = extendedN.length();
// We need to set this value on the shader prior to drawing
boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
n += extendedN;
}
+
float x = n.x;
n.x = -n.y;
n.y = x;
@@ -1892,6 +1922,7 @@
vec2 abVector = (b - a);
length = abVector.length();
abVector.normalize();
+
if (scaled) {
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
@@ -1900,6 +1931,7 @@
} else {
boundaryLengthProportion = .5 / (length + 1);
}
+
abVector /= 2;
a -= abVector;
b += abVector;
@@ -1928,10 +1960,12 @@
Vertex::set(vertices++, p1.x, p1.y);
generatedVerticesCount += 2;
}
+
Vertex::set(vertices++, p1.x, p1.y);
Vertex::set(vertices++, p2.x, p2.y);
Vertex::set(vertices++, p4.x, p4.y);
Vertex::set(vertices++, p3.x, p3.y);
+
prevVertex = vertices - 1;
generatedVerticesCount += 4;
} else {
@@ -1944,14 +1978,17 @@
inverseBoundaryWidthSlot =
mCaches.currentProgram->getUniform("inverseBoundaryWidth");
}
+
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
}
+
if (boundaryLengthSlot < 0) {
boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
inverseBoundaryLengthSlot =
mCaches.currentProgram->getUniform("inverseBoundaryLength");
}
+
glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
@@ -1965,21 +2002,29 @@
AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
generatedVerticesCount += 2;
}
+
AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
+
prevAAVertex = aaVertices - 1;
generatedVerticesCount += 4;
}
+
dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
*mSnapshot->transform);
}
}
+
if (generatedVerticesCount > 0) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
}
+
+ if (isAA) {
+ finishDrawAALine(widthSlot, lengthSlot);
+ }
}
void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
@@ -2025,10 +2070,12 @@
for (int i = 0; i < count; i += 2) {
TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
generatedVerticesCount++;
+
float left = points[i] - halfWidth;
float right = points[i] + halfWidth;
float top = points[i + 1] - halfWidth;
float bottom = points [i + 1] + halfWidth;
+
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index ab137cc..b52d2b0 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -553,7 +553,8 @@
void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
void setupDrawVertices(GLvoid* vertices);
void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
- float strokeWidth);
+ float strokeWidth, int& widthSlot, int& lengthSlot);
+ void finishDrawAALine(const int widthSlot, const int lengthSlot);
void finishDrawTexture();
void accountForClear(SkXfermode::Mode mode);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c66a03f..f59848f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -126,6 +126,7 @@
private static final int MSG_RCDISPLAY_CLEAR = 13;
private static final int MSG_RCDISPLAY_UPDATE = 14;
private static final int MSG_SET_ALL_VOLUMES = 15;
+ private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 16;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -501,6 +502,10 @@
System.MUTE_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
+ boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
+ AudioSystem.setMasterMute(masterMute);
+ broadcastMasterMuteStatus(masterMute);
+
// Each stream will read its own persisted settings
// Broadcast the sticky intent
@@ -740,9 +745,14 @@
// UI update and Broadcast Intent
private void sendMasterMuteUpdate(boolean muted, int flags) {
mVolumePanel.postMasterMuteChanged(flags);
+ broadcastMasterMuteStatus(muted);
+ }
+ private void broadcastMasterMuteStatus(boolean muted) {
Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
long origCallerIdentityToken = Binder.clearCallingIdentity();
mContext.sendStickyBroadcast(intent);
Binder.restoreCallingIdentity(origCallerIdentityToken);
@@ -820,6 +830,9 @@
public void setMasterMute(boolean state, IBinder cb) {
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
+ // Post a persist master volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
+ : 0, 0, null, PERSIST_DELAY);
sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
}
}
@@ -2551,6 +2564,11 @@
(float)msg.arg1 / (float)1000.0);
break;
+ case MSG_PERSIST_MASTER_VOLUME_MUTE:
+ Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
+ msg.arg1);
+ break;
+
case MSG_PERSIST_RINGER_MODE:
// note that the value persisted is the current ringer mode, not the
// value of ringer mode as of the time the request was made to persist
@@ -3038,11 +3056,6 @@
adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
BluetoothProfile.A2DP);
}
-
- if (mUseMasterVolume) {
- // Send sticky broadcast for initial master mute state
- sendMasterMuteUpdate(false, 0);
- }
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java
new file mode 100644
index 0000000..43e34fb
--- /dev/null
+++ b/media/java/android/media/Crypto.java
@@ -0,0 +1,49 @@
+/*
+ * 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.media;
+
+/**
+ * Crypto class can be used in conjunction with MediaCodec to decode
+ * encrypted media data.
+ * @hide
+*/
+public final class Crypto {
+ public static final native boolean isCryptoSchemeSupported(byte[] uuid);
+
+ public Crypto(byte[] uuid, byte[] initData) {
+ native_setup(uuid, initData);
+ }
+
+ public final native boolean requiresSecureDecoderComponent(String mime);
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ public native final void release();
+ private static native final void native_init();
+ private native final void native_setup(byte[] uuid, byte[] initData);
+ private native final void native_finalize();
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7629d60..66cea9d4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,7 @@
package android.media;
+import android.media.Crypto;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -25,8 +26,7 @@
* encoder/decoder components.
* @hide
*/
-public class MediaCodec
-{
+final public class MediaCodec {
/** Per buffer metadata includes an offset and size specifying
the range of valid data in the associated codec buffer.
*/
@@ -113,11 +113,14 @@
*
* @param surface Specify a surface on which to render the output of this
* decoder.
+ * @param crypto Specify a crypto object to facilitate secure decryption
+ * of the media data.
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
*/
public void configure(
- Map<String, Object> format, Surface surface, int flags) {
+ Map<String, Object> format,
+ Surface surface, Crypto crypto, int flags) {
String[] keys = null;
Object[] values = null;
@@ -133,11 +136,12 @@
}
}
- native_configure(keys, values, surface, flags);
+ native_configure(keys, values, surface, crypto, flags);
}
private native final void native_configure(
- String[] keys, Object[] values, Surface surface, int flags);
+ String[] keys, Object[] values,
+ Surface surface, Crypto crypto, int flags);
/** After successfully configuring the component, call start. On return
* you can query the component for its input/output buffers.
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9ea3d0e..9c3b6a7 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -23,8 +23,7 @@
* MediaExtractor
* @hide
*/
-public class MediaExtractor
-{
+final public class MediaExtractor {
public MediaExtractor(String path) {
native_setup(path);
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 2f4ed89..26089ad 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -609,6 +609,10 @@
mCompilation = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("isdrm")) {
mIsDrm = (parseSubstring(value, 0, 0) == 1);
+ } else if (name.equalsIgnoreCase("width")) {
+ mWidth = parseSubstring(value, 0, 0);
+ } else if (name.equalsIgnoreCase("height")) {
+ mHeight = parseSubstring(value, 0, 0);
} else {
//Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
}
@@ -734,9 +738,11 @@
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
+ String resolution = null;
if (mWidth > 0 && mHeight > 0) {
map.put(MediaStore.MediaColumns.WIDTH, mWidth);
map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
+ resolution = mWidth + "x" + mHeight;
}
if (!mNoMedia) {
@@ -746,7 +752,9 @@
map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
? mAlbum : MediaStore.UNKNOWN_STRING));
map.put(Video.Media.DURATION, mDuration);
- // FIXME - add RESOLUTION
+ if (resolution != null) {
+ map.put(Video.Media.RESOLUTION, resolution);
+ }
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index dd1e505..a3361d4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_Crypto.cpp \
android_media_MediaCodec.cpp \
android_media_MediaCodecList.cpp \
android_media_MediaExtractor.cpp \
diff --git a/media/jni/android_media_Crypto.cpp b/media/jni/android_media_Crypto.cpp
new file mode 100644
index 0000000..e1a60a1
--- /dev/null
+++ b/media/jni/android_media_Crypto.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto-JNI"
+#include <utils/Log.h>
+
+#include "android_media_Crypto.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
+ return (JCrypto *)env->GetIntField(thiz, gFields.context);
+}
+
+JCrypto::JCrypto(
+ JNIEnv *env, jobject thiz,
+ const uint8_t uuid[16], const void *initData, size_t initSize) {
+ mObject = env->NewWeakGlobalRef(thiz);
+
+ mCrypto = MakeCrypto(uuid, initData, initSize);
+}
+
+JCrypto::~JCrypto() {
+ mCrypto.clear();
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ if (service == NULL) {
+ return NULL;
+ }
+
+ sp<ICrypto> crypto = service->makeCrypto();
+
+ if (crypto == NULL || crypto->initCheck() != OK) {
+ return NULL;
+ }
+
+ return crypto;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto(
+ const uint8_t uuid[16], const void *initData, size_t initSize) {
+ sp<ICrypto> crypto = MakeCrypto();
+
+ if (crypto == NULL) {
+ return NULL;
+ }
+
+ status_t err = crypto->createPlugin(uuid, initData, initSize);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ return crypto;
+}
+
+bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
+ if (mCrypto == NULL) {
+ return false;
+ }
+
+ return mCrypto->requiresSecureDecoderComponent(mime);
+}
+
+// static
+bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
+ sp<ICrypto> crypto = MakeCrypto();
+
+ if (crypto == NULL) {
+ return false;
+ }
+
+ return crypto->isCryptoSchemeSupported(uuid);
+}
+
+status_t JCrypto::initCheck() const {
+ return mCrypto == NULL ? NO_INIT : OK;
+}
+
+// static
+sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
+ jclass clazz = env->FindClass("android/media/Crypto");
+ CHECK(clazz != NULL);
+
+ if (!env->IsInstanceOf(obj, clazz)) {
+ return NULL;
+ }
+
+ sp<JCrypto> jcrypto = getCrypto(env, obj);
+
+ if (jcrypto == NULL) {
+ return NULL;
+ }
+
+ return jcrypto->mCrypto;
+}
+
+} // namespace android
+
+using namespace android;
+
+static sp<JCrypto> setCrypto(
+ JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
+ sp<JCrypto> old = (JCrypto *)env->GetIntField(thiz, gFields.context);
+ if (crypto != NULL) {
+ crypto->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetIntField(thiz, gFields.context, (int)crypto.get());
+
+ return old;
+}
+
+static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
+ setCrypto(env, thiz, NULL);
+}
+
+static void android_media_Crypto_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/Crypto");
+ CHECK(clazz != NULL);
+
+ gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ CHECK(gFields.context != NULL);
+}
+
+static void android_media_Crypto_native_setup(
+ JNIEnv *env, jobject thiz,
+ jbyteArray uuidObj, jbyteArray initDataObj) {
+ jsize uuidLength = env->GetArrayLength(uuidObj);
+
+ if (uuidLength != 16) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ NULL);
+ return;
+ }
+
+ jboolean isCopy;
+ jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+ jsize initDataLength = env->GetArrayLength(initDataObj);
+ jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);
+
+ sp<JCrypto> crypto = new JCrypto(
+ env, thiz, (const uint8_t *)uuid, initData, initDataLength);
+
+ status_t err = crypto->initCheck();
+
+ env->ReleaseByteArrayElements(initDataObj, initData, 0);
+ initData = NULL;
+
+ env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+ uuid = NULL;
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate crypto object.");
+ return;
+ }
+
+ setCrypto(env,thiz, crypto);
+}
+
+static void android_media_Crypto_native_finalize(
+ JNIEnv *env, jobject thiz) {
+ android_media_Crypto_release(env, thiz);
+}
+
+static jboolean android_media_Crypto_isCryptoSchemeSupported(
+ JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
+ jsize uuidLength = env->GetArrayLength(uuidObj);
+
+ if (uuidLength != 16) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ NULL);
+ return false;
+ }
+
+ jboolean isCopy;
+ jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+ bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
+
+ env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+ uuid = NULL;
+
+ return result;
+}
+
+static jboolean android_media_Crypto_requiresSecureDecoderComponent(
+ JNIEnv *env, jobject thiz, jstring mimeObj) {
+ if (mimeObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ sp<JCrypto> crypto = getCrypto(env, thiz);
+
+ if (crypto == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ const char *mime = env->GetStringUTFChars(mimeObj, NULL);
+
+ if (mime == NULL) {
+ return false;
+ }
+
+ bool result = crypto->requiresSecureDecoderComponent(mime);
+
+ env->ReleaseStringUTFChars(mimeObj, mime);
+ mime = NULL;
+
+ return result;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "release", "()V", (void *)android_media_Crypto_release },
+ { "native_init", "()V", (void *)android_media_Crypto_native_init },
+
+ { "native_setup", "([B[B)V",
+ (void *)android_media_Crypto_native_setup },
+
+ { "native_finalize", "()V",
+ (void *)android_media_Crypto_native_finalize },
+
+ { "isCryptoSchemeSupported", "([B)Z",
+ (void *)android_media_Crypto_isCryptoSchemeSupported },
+
+ { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
+ (void *)android_media_Crypto_requiresSecureDecoderComponent },
+};
+
+int register_android_media_Crypto(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/Crypto", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_Crypto.h b/media/jni/android_media_Crypto.h
new file mode 100644
index 0000000..505725e
--- /dev/null
+++ b/media/jni/android_media_Crypto.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_CRYPTO_H_
+#define _ANDROID_MEDIA_CRYPTO_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ICrypto;
+
+struct JCrypto : public RefBase {
+ static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
+
+ JCrypto(JNIEnv *env, jobject thiz,
+ const uint8_t uuid[16], const void *initData, size_t initSize);
+
+ status_t initCheck() const;
+
+ bool requiresSecureDecoderComponent(const char *mime) const;
+
+ static sp<ICrypto> GetCrypto(JNIEnv *env, jobject obj);
+
+protected:
+ virtual ~JCrypto();
+
+private:
+ jweak mObject;
+ sp<ICrypto> mCrypto;
+
+ static sp<ICrypto> MakeCrypto();
+
+ static sp<ICrypto> MakeCrypto(
+ const uint8_t uuid[16], const void *initData, size_t initSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JCrypto);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_CRYPTO_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4b7a811..217216a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -20,6 +20,7 @@
#include "android_media_MediaCodec.h"
+#include "android_media_Crypto.h"
#include "android_media_Utils.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
@@ -98,12 +99,13 @@
status_t JMediaCodec::configure(
const sp<AMessage> &format,
const sp<ISurfaceTexture> &surfaceTexture,
+ const sp<ICrypto> &crypto,
int flags) {
sp<SurfaceTextureClient> client;
if (surfaceTexture != NULL) {
client = new SurfaceTextureClient(surfaceTexture);
}
- return mCodec->configure(format, client, NULL /* crypto */, flags);
+ return mCodec->configure(format, client, crypto, flags);
}
status_t JMediaCodec::start() {
@@ -256,6 +258,7 @@
jobject thiz,
jobjectArray keys, jobjectArray values,
jobject jsurface,
+ jobject jcrypto,
jint flags) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -286,7 +289,12 @@
}
}
- err = codec->configure(format, surfaceTexture, flags);
+ sp<ICrypto> crypto;
+ if (jcrypto != NULL) {
+ crypto = JCrypto::GetCrypto(env, jcrypto);
+ }
+
+ err = codec->configure(format, surfaceTexture, crypto, flags);
throwExceptionAsNecessary(env, err);
}
@@ -513,7 +521,8 @@
{ "release", "()V", (void *)android_media_MediaCodec_release },
{ "native_configure",
- "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+ "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
+ "Landroid/media/Crypto;I)V",
(void *)android_media_MediaCodec_native_configure },
{ "start", "()V", (void *)android_media_MediaCodec_start },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6b1257d..6bb4071 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,7 @@
struct ALooper;
struct AMessage;
+struct ICrypto;
struct ISurfaceTexture;
struct MediaCodec;
@@ -40,6 +41,7 @@
status_t configure(
const sp<AMessage> &format,
const sp<ISurfaceTexture> &surfaceTexture,
+ const sp<ICrypto> &crypto,
int flags);
status_t start();
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 3074bb1..2e74ffd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -879,6 +879,7 @@
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
+extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_MediaCodec(JNIEnv *env);
extern int register_android_media_MediaExtractor(JNIEnv *env);
extern int register_android_media_MediaCodecList(JNIEnv *env);
@@ -968,6 +969,11 @@
goto bail;
}
+ if (register_android_media_Crypto(env) < 0) {
+ ALOGE("ERROR: MediaCodec native registration failed");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
new file mode 100644
index 0000000..768be7d
--- /dev/null
+++ b/services/java/com/android/server/NsdService.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.nsd.DnsSdServiceInfo;
+import android.net.nsd.DnsSdTxtRecord;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.IBinder;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.List;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.NativeDaemonConnector.Command;
+import com.android.internal.R;
+
+/**
+ * Network Service Discovery Service handles remote service discovery operation requests by
+ * implementing the INsdManager interface.
+ *
+ * @hide
+ */
+public class NsdService extends INsdManager.Stub {
+ private static final String TAG = "NsdService";
+ private static final String MDNS_TAG = "mDnsConnector";
+
+ private static final boolean DBG = true;
+
+ private Context mContext;
+
+ /**
+ * Clients receiving asynchronous messages
+ */
+ private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
+
+ private AsyncChannel mReplyChannel = new AsyncChannel();
+
+ /**
+ * Handles client(app) connections
+ */
+ private class AsyncServiceHandler extends Handler {
+
+ AsyncServiceHandler(android.os.Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ AsyncChannel c = (AsyncChannel) msg.obj;
+ if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
+ c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
+ mClients.add(c);
+ } else {
+ Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.e(TAG, "Send failed, client connection lost");
+ } else {
+ if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ }
+ mClients.remove((AsyncChannel) msg.obj);
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ AsyncChannel ac = new AsyncChannel();
+ ac.connect(mContext, this, msg.replyTo);
+ break;
+ case NsdManager.DISCOVER_SERVICES:
+ if (DBG) Slog.d(TAG, "Discover services");
+ DnsSdServiceInfo s = (DnsSdServiceInfo) msg.obj;
+ discoverServices(1, s.getServiceType());
+ mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED);
+ break;
+ case NsdManager.STOP_DISCOVERY:
+ if (DBG) Slog.d(TAG, "Stop service discovery");
+ mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED);
+ break;
+ case NsdManager.REGISTER_SERVICE:
+ if (DBG) Slog.d(TAG, "Register service");
+ mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED);
+ break;
+ case NsdManager.UPDATE_SERVICE:
+ if (DBG) Slog.d(TAG, "Update service");
+ mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
+ break;
+ default:
+ Slog.d(TAG, "NsdServicehandler.handleMessage ignoring msg=" + msg);
+ break;
+ }
+ }
+ }
+ private AsyncServiceHandler mAsyncServiceHandler;
+
+ private NativeDaemonConnector mNativeConnector;
+ private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
+
+ private NsdService(Context context) {
+ mContext = context;
+
+ HandlerThread nsdThread = new HandlerThread("NsdService");
+ nsdThread.start();
+ mAsyncServiceHandler = new AsyncServiceHandler(nsdThread.getLooper());
+
+ /*
+ mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
+ MDNS_TAG, 25);
+ Thread th = new Thread(mNativeConnector, MDNS_TAG);
+ th.start();
+ */
+ }
+
+ public static NsdService create(Context context) throws InterruptedException {
+ NsdService service = new NsdService(context);
+ /* service.mNativeDaemonConnected.await(); */
+ return service;
+ }
+
+ public Messenger getMessenger() {
+ return new Messenger(mAsyncServiceHandler);
+ }
+
+ /* These should be in sync with system/netd/mDnsResponseCode.h */
+ class NativeResponseCode {
+ public static final int SERVICE_FOUND = 101;
+ public static final int SERVICE_LOST = 102;
+ public static final int SERVICE_DISCOVERY_FAILED = 103;
+
+ public static final int SERVICE_REGISTERED = 104;
+ public static final int SERVICE_REGISTRATION_FAILED = 105;
+
+ public static final int SERVICE_UPDATED = 106;
+ public static final int SERVICE_UPDATE_FAILED = 107;
+
+ public static final int SERVICE_RESOLVED = 108;
+ public static final int SERVICE_RESOLUTION_FAILED = 109;
+ }
+
+
+ class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
+ public void onDaemonConnected() {
+ mNativeDaemonConnected.countDown();
+ }
+
+ public boolean onEvent(int code, String raw, String[] cooked) {
+ switch (code) {
+ case NativeResponseCode.SERVICE_FOUND:
+ /* NNN uniqueId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_LOST:
+ /* NNN uniqueId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
+ /* NNN uniqueId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_REGISTERED:
+ /* NNN regId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
+ /* NNN regId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_UPDATED:
+ /* NNN regId */
+ break;
+ case NativeResponseCode.SERVICE_UPDATE_FAILED:
+ /* NNN regId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_RESOLVED:
+ /* NNN resolveId fullName hostName port txtlen txtdata */
+ break;
+ case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
+ /* NNN resovleId errorCode */
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ }
+
+ private void registerService(int regId, DnsSdServiceInfo service) {
+ try {
+ //Add txtlen and txtdata
+ mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
+ service.getServiceType(), service.getPort());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to execute registerService");
+ }
+ }
+
+ private void updateService(int regId, DnsSdTxtRecord t) {
+ try {
+ if (t == null) return;
+ mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to updateServices");
+ }
+ }
+
+ private void discoverServices(int discoveryId, String serviceType) {
+ try {
+ mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to discoverServices");
+ }
+ }
+
+ private void stopServiceDiscovery(int discoveryId) {
+ try {
+ mNativeConnector.execute("mdnssd", "stopdiscover", discoveryId);
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to stopServiceDiscovery");
+ }
+ }
+
+ private void resolveService(DnsSdServiceInfo service) {
+ try {
+ mNativeConnector.execute("mdnssd", "resolve", service.getServiceName(),
+ service.getServiceType());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to resolveService");
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("Internal state:");
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 423dad6..e091edf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -120,6 +120,7 @@
ConnectivityService connectivity = null;
WifiP2pService wifiP2p = null;
WifiService wifi = null;
+ NsdService serviceDiscovery= null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
@@ -394,6 +395,15 @@
}
try {
+ Slog.i(TAG, "Network Service Discovery Service");
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
+ }
+
+ try {
Slog.i(TAG, "Throttle Service");
throttle = new ThrottleService(context);
ServiceManager.addService(
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
new file mode 100644
index 0000000..6ba4c35
--- /dev/null
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -0,0 +1,288 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import android.graphics.Matrix;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+public class AppWindowAnimator {
+
+ final AppWindowToken mAppToken;
+ final WindowManagerService mService;
+ final WindowAnimator mAnimator;
+
+ boolean animating;
+ Animation animation;
+ boolean animInitialized;
+ boolean hasTransformation;
+ final Transformation transformation = new Transformation();
+
+ // Have we been asked to have this token keep the screen frozen?
+ // Protect with mAnimator.
+ boolean freezingScreen;
+
+ // Offset to the window of all layers in the token, for use by
+ // AppWindowToken animations.
+ int animLayerAdjustment;
+
+ // Special surface for thumbnail animation.
+ Surface thumbnail;
+ int thumbnailTransactionSeq;
+ int thumbnailX;
+ int thumbnailY;
+ int thumbnailLayer;
+ Animation thumbnailAnimation;
+ final Transformation thumbnailTransformation = new Transformation();
+
+ public AppWindowAnimator(final WindowManagerService service, final AppWindowToken atoken) {
+ mService = service;
+ mAppToken = atoken;
+ mAnimator = service.mAnimator;
+ }
+
+ public void setAnimation(Animation anim, boolean initialized) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+ animation = anim;
+ animating = false;
+ animInitialized = initialized;
+ anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mService.mTransitionAnimationScale);
+ int zorder = anim.getZAdjustment();
+ int adj = 0;
+ if (zorder == Animation.ZORDER_TOP) {
+ adj = WindowManagerService.TYPE_LAYER_OFFSET;
+ } else if (zorder == Animation.ZORDER_BOTTOM) {
+ adj = -WindowManagerService.TYPE_LAYER_OFFSET;
+ }
+
+ if (animLayerAdjustment != adj) {
+ animLayerAdjustment = adj;
+ updateLayers();
+ }
+ // Start out animation gone if window is gone, or visible if window is visible.
+ transformation.clear();
+ transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
+ hasTransformation = true;
+ }
+
+ public void setDummyAnimation() {
+ if (animation == null) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting dummy animation in " + this);
+ animation = WindowManagerService.sDummyAnimation;
+ animInitialized = false;
+ }
+ }
+
+ public void clearAnimation() {
+ if (animation != null) {
+ animation = null;
+ animating = true;
+ animInitialized = false;
+ }
+ clearThumbnail();
+ }
+
+ public void clearThumbnail() {
+ if (thumbnail != null) {
+ thumbnail.destroy();
+ thumbnail = null;
+ }
+ }
+
+ void updateLayers() {
+ final int N = mAppToken.allAppWindows.size();
+ final int adj = animLayerAdjustment;
+ thumbnailLayer = -1;
+ for (int i=0; i<N; i++) {
+ final WindowState w = mAppToken.allAppWindows.get(i);
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ winAnimator.mAnimLayer = w.mLayer + adj;
+ if (winAnimator.mAnimLayer > thumbnailLayer) {
+ thumbnailLayer = winAnimator.mAnimLayer;
+ }
+ if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
+ + winAnimator.mAnimLayer);
+ if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
+ mService.setInputMethodAnimLayerAdjustment(adj);
+ }
+ if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) {
+ mService.setWallpaperAnimLayerAdjustmentLocked(adj);
+ }
+ }
+ }
+
+ private void stepThumbnailAnimation(long currentTime) {
+ thumbnailTransformation.clear();
+ thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+ thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
+ final boolean screenAnimation = mAnimator.mScreenRotationAnimation != null
+ && mAnimator.mScreenRotationAnimation.isAnimating();
+ if (screenAnimation) {
+ thumbnailTransformation.postCompose(
+ mAnimator.mScreenRotationAnimation.getEnterTransformation());
+ }
+ // cache often used attributes locally
+ final float tmpFloats[] = mService.mTmpFloats;
+ thumbnailTransformation.getMatrix().getValues(tmpFloats);
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+ "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+ + ", " + tmpFloats[Matrix.MTRANS_Y], null);
+ thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+ "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+ + " layer=" + thumbnailLayer
+ + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ + "," + tmpFloats[Matrix.MSKEW_Y]
+ + "][" + tmpFloats[Matrix.MSKEW_X]
+ + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
+ thumbnail.setAlpha(thumbnailTransformation.getAlpha());
+ // The thumbnail is layered below the window immediately above this
+ // token's anim layer.
+ thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+ tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ }
+
+ private boolean stepAnimation(long currentTime) {
+ if (animation == null) {
+ return false;
+ }
+ transformation.clear();
+ final boolean more = animation.getTransformation(currentTime, transformation);
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + transformation);
+ if (!more) {
+ animation = null;
+ clearThumbnail();
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+ }
+ hasTransformation = more;
+ return more;
+ }
+
+ // This must be called while inside a transaction.
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+ if (mService.okToDisplay()) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (animation == WindowManagerService.sDummyAnimation) {
+ // This guy is going to animate, but not yet. For now count
+ // it as not animating for purposes of scheduling transactions;
+ // when it is really time to animate, this will be set to
+ // a real animation and the next call will execute normally.
+ return false;
+ }
+
+ if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+ && animation != null) {
+ if (!animating) {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": dw=" + dw + " dh=" + dh
+ + " scale=" + mService.mTransitionAnimationScale
+ + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
+ if (!animInitialized) {
+ animation.initialize(dw, dh, dw, dh);
+ }
+ animation.setStartTime(currentTime);
+ animating = true;
+ if (thumbnail != null) {
+ thumbnail.show();
+ thumbnailAnimation.setStartTime(currentTime);
+ }
+ }
+ if (stepAnimation(currentTime)) {
+ // animation isn't over, step any thumbnail and that's
+ // it for now.
+ if (thumbnail != null) {
+ stepThumbnailAnimation(currentTime);
+ }
+ return true;
+ }
+ }
+ } else if (animation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ animating = true;
+ animation = null;
+ }
+
+ hasTransformation = false;
+
+ if (!animating) {
+ return false;
+ }
+
+ mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
+ }
+
+ clearAnimation();
+ animating = false;
+ if (animLayerAdjustment != 0) {
+ animLayerAdjustment = 0;
+ updateLayers();
+ }
+ if (mService.mInputMethodTarget != null
+ && mService.mInputMethodTarget.mAppToken == mAppToken) {
+ mService.moveInputMethodWindowsIfNeededLocked(true);
+ }
+
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Animation done in " + this
+ + ": reportedVisible=" + mAppToken.reportedVisible);
+
+ transformation.clear();
+
+ final int N = mAppToken.windows.size();
+ for (int i=0; i<N; i++) {
+ mAppToken.windows.get(i).mWinAnimator.finishExit();
+ }
+ mAppToken.updateReportedVisibilityLocked();
+
+ return false;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ if (freezingScreen) {
+ pw.print(prefix); pw.print(" freezingScreen="); pw.println(freezingScreen);
+ }
+ if (animating || animation != null) {
+ pw.print(prefix); pw.print("animating="); pw.print(animating);
+ 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);
+ pw.print(" y="); pw.print(thumbnailY);
+ pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
+ pw.print(prefix); pw.print("thumbnailTransformation=");
+ pw.println(thumbnailTransformation.toShortString());
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 3069b74..e3d46d8 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -21,17 +21,12 @@
import com.android.server.wm.WindowManagerService.H;
import android.content.pm.ActivityInfo;
-import android.graphics.Matrix;
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IApplicationToken;
-import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -47,6 +42,9 @@
// All of the windows and child windows that are included in this
// application token. Note this list is NOT sorted!
final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
+ final AppWindowAnimator mAppAnimator;
+
+ final WindowAnimator mAnimator;
int groupId = -1;
boolean appFullscreen;
@@ -87,19 +85,6 @@
// Set to true when the token has been removed from the window mgr.
boolean removed;
- // Have we been asked to have this token keep the screen frozen?
- boolean freezingScreen;
-
- boolean animating;
- Animation animation;
- boolean animInitialized;
- boolean hasTransformation;
- final Transformation transformation = new Transformation();
-
- // Offset to the window of all layers in the token, for use by
- // AppWindowToken animations.
- int animLayerAdjustment;
-
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
@@ -108,15 +93,6 @@
boolean startingMoved;
boolean firstWindowDrawn;
- // Special surface for thumbnail animation.
- Surface thumbnail;
- int thumbnailTransactionSeq;
- int thumbnailX;
- int thumbnailY;
- int thumbnailLayer;
- Animation thumbnailAnimation;
- final Transformation thumbnailTransformation = new Transformation();
-
// Input application handle used by the input dispatcher.
final InputApplicationHandle mInputApplicationHandle;
@@ -126,79 +102,8 @@
appWindowToken = this;
appToken = _token;
mInputApplicationHandle = new InputApplicationHandle(this);
- }
-
- public void setAnimation(Animation anim, boolean initialized) {
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
- animation = anim;
- animating = false;
- animInitialized = initialized;
- anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(service.mTransitionAnimationScale);
- int zorder = anim.getZAdjustment();
- int adj = 0;
- if (zorder == Animation.ZORDER_TOP) {
- adj = WindowManagerService.TYPE_LAYER_OFFSET;
- } else if (zorder == Animation.ZORDER_BOTTOM) {
- adj = -WindowManagerService.TYPE_LAYER_OFFSET;
- }
-
- if (animLayerAdjustment != adj) {
- animLayerAdjustment = adj;
- updateLayers();
- }
- // Start out animation gone if window is gone, or visible if window is visible.
- transformation.clear();
- transformation.setAlpha(reportedVisible ? 1 : 0);
- hasTransformation = true;
- }
-
- public void setDummyAnimation() {
- if (animation == null) {
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Setting dummy animation in " + this);
- animation = WindowManagerService.sDummyAnimation;
- animInitialized = false;
- }
- }
-
- public void clearAnimation() {
- if (animation != null) {
- animation = null;
- animating = true;
- animInitialized = false;
- }
- clearThumbnail();
- }
-
- public void clearThumbnail() {
- if (thumbnail != null) {
- thumbnail.destroy();
- thumbnail = null;
- }
- }
-
- void updateLayers() {
- final int N = allAppWindows.size();
- final int adj = animLayerAdjustment;
- thumbnailLayer = -1;
- for (int i=0; i<N; i++) {
- final WindowState w = allAppWindows.get(i);
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- winAnimator.mAnimLayer = w.mLayer + adj;
- if (winAnimator.mAnimLayer > thumbnailLayer) {
- thumbnailLayer = winAnimator.mAnimLayer;
- }
- if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
- + winAnimator.mAnimLayer);
- if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
- service.setInputMethodAnimLayerAdjustment(adj);
- }
- if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) {
- service.setWallpaperAnimLayerAdjustmentLocked(adj);
- }
- }
+ mAnimator = service.mAnimator;
+ mAppAnimator = new AppWindowAnimator(_service, this);
}
void sendAppVisibilityToClients() {
@@ -231,141 +136,6 @@
return isAnimating;
}
- private void stepThumbnailAnimation(long currentTime) {
- thumbnailTransformation.clear();
- thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
- thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
- final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null
- && service.mAnimator.mScreenRotationAnimation.isAnimating();
- if (screenAnimation) {
- thumbnailTransformation.postCompose(
- service.mAnimator.mScreenRotationAnimation.getEnterTransformation());
- }
- // cache often used attributes locally
- final float tmpFloats[] = service.mTmpFloats;
- thumbnailTransformation.getMatrix().getValues(tmpFloats);
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
- "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
- + ", " + tmpFloats[Matrix.MTRANS_Y], null);
- thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
- "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
- + " layer=" + thumbnailLayer
- + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
- + "," + tmpFloats[Matrix.MSKEW_Y]
- + "][" + tmpFloats[Matrix.MSKEW_X]
- + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
- thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- // The thumbnail is layered below the window immediately above this
- // token's anim layer.
- thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
- tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
- }
-
- private boolean stepAnimation(long currentTime) {
- if (animation == null) {
- return false;
- }
- transformation.clear();
- final boolean more = animation.getTransformation(currentTime, transformation);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + transformation);
- if (!more) {
- animation = null;
- clearThumbnail();
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Finished animation in " + this +
- " @ " + currentTime);
- }
- hasTransformation = more;
- return more;
- }
-
- // This must be called while inside a transaction.
- boolean stepAnimationLocked(long currentTime, int dw, int dh) {
- if (service.okToDisplay()) {
- // We will run animations as long as the display isn't frozen.
-
- if (animation == WindowManagerService.sDummyAnimation) {
- // This guy is going to animate, but not yet. For now count
- // it as not animating for purposes of scheduling transactions;
- // when it is really time to animate, this will be set to
- // a real animation and the next call will execute normally.
- return false;
- }
-
- if ((allDrawn || animating || startingDisplayed) && animation != null) {
- if (!animating) {
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Starting animation in " + this +
- " @ " + currentTime + ": dw=" + dw + " dh=" + dh
- + " scale=" + service.mTransitionAnimationScale
- + " allDrawn=" + allDrawn + " animating=" + animating);
- if (!animInitialized) {
- animation.initialize(dw, dh, dw, dh);
- }
- animation.setStartTime(currentTime);
- animating = true;
- if (thumbnail != null) {
- thumbnail.show();
- thumbnailAnimation.setStartTime(currentTime);
- }
- }
- if (stepAnimation(currentTime)) {
- // animation isn't over, step any thumbnail and that's
- // it for now.
- if (thumbnail != null) {
- stepThumbnailAnimation(currentTime);
- }
- return true;
- }
- }
- } else if (animation != null) {
- // If the display is frozen, and there is a pending animation,
- // clear it and make sure we run the cleanup code.
- animating = true;
- animation = null;
- }
-
- hasTransformation = false;
-
- if (!animating) {
- return false;
- }
-
- service.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- service.debugLayoutRepeats("AppWindowToken");
- }
-
- clearAnimation();
- animating = false;
- if (animLayerAdjustment != 0) {
- animLayerAdjustment = 0;
- updateLayers();
- }
- if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) {
- service.moveInputMethodWindowsIfNeededLocked(true);
- }
-
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Animation done in " + this
- + ": reportedVisible=" + reportedVisible);
-
- transformation.clear();
-
- final int N = windows.size();
- for (int i=0; i<N; i++) {
- windows.get(i).mWinAnimator.finishExit();
- }
- updateReportedVisibilityLocked();
-
- return false;
- }
-
void updateReportedVisibilityLocked() {
if (appToken == null) {
return;
@@ -479,9 +249,8 @@
pw.print(" willBeHidden="); pw.print(willBeHidden);
pw.print(" reportedDrawn="); pw.print(reportedDrawn);
pw.print(" reportedVisible="); pw.println(reportedVisible);
- if (paused || freezingScreen) {
- pw.print(prefix); pw.print("paused="); pw.print(paused);
- pw.print(" freezingScreen="); pw.println(freezingScreen);
+ if (paused) {
+ pw.print(prefix); pw.print("paused="); pw.println(paused);
}
if (numInterestingWindows != 0 || numDrawnWindows != 0
|| inPendingTransaction || allDrawn) {
@@ -491,18 +260,6 @@
pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
pw.print(" allDrawn="); pw.println(allDrawn);
}
- if (animating || animation != null) {
- pw.print(prefix); pw.print("animating="); pw.print(animating);
- 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 (startingData != null || removed || firstWindowDrawn) {
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
pw.print(" removed="); pw.print(removed);
@@ -515,15 +272,6 @@
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved"); pw.println(startingMoved);
}
- if (thumbnail != null) {
- pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" x="); pw.print(thumbnailX);
- pw.print(" y="); pw.print(thumbnailY);
- pw.print(" layer="); pw.println(thumbnailLayer);
- pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
- pw.print(prefix); pw.print("thumbnailTransformation=");
- pw.println(thumbnailTransformation.toShortString());
- }
}
@Override
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 0051d98..405dd04 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -57,12 +57,17 @@
}
/**
- * Show the dim surface.
+ * Set's the dim surface's layer and update dim parameters that will be used in
+ * {@link #updateSurface} after all windows are examined.
*/
- void show(int dw, int dh) {
+ void updateParameters(final Resources res, final Parameters params, final long currentTime) {
+ final int dw = params.mDimWidth;
+ final int dh = params.mDimHeight;
+ final WindowStateAnimator winAnimator = params.mDimWinAnimator;
+ final float target = params.mDimTarget;
if (!mDimShown) {
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
- dw + "x" + dh + ")");
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + dw + "x" + dh + ")");
mDimShown = true;
try {
mLastDimWidth = dw;
@@ -78,17 +83,9 @@
mLastDimHeight = dh;
mDimSurface.setSize(dw, dh);
}
- }
- /**
- * Set's the dim surface's layer and update dim parameters that will be used in
- * {@link #updateSurface} after all windows are examined.
- */
- void updateParameters(Resources res, WindowState w, long currentTime) {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
mDimSurface.setLayer(winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
- final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
+ mDimSurface + ": layer=" + (winAnimator.mAnimLayer-1) + " target=" + target);
if (mDimTargetAlpha != target) {
@@ -189,4 +186,18 @@
pw.print(" delta="); pw.print(mDimDeltaPerMs);
pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
}
+
+ static class Parameters {
+ final WindowStateAnimator mDimWinAnimator;
+ final int mDimWidth;
+ final int mDimHeight;
+ final float mDimTarget;
+ Parameters(final WindowStateAnimator dimWinAnimator, final int dimWidth,
+ final int dimHeight, final float dimTarget) {
+ mDimWinAnimator = dimWinAnimator;
+ mDimWidth = dimWidth;
+ mDimHeight = dimHeight;
+ mDimTarget = dimTarget;
+ }
+ }
}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 5a104b2..77f94d9 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -6,6 +6,9 @@
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
+
+import static com.android.server.wm.WindowManagerService.H.SET_DIM_PARAMETERS;
import android.content.Context;
import android.os.SystemClock;
@@ -21,7 +24,6 @@
import java.io.PrintWriter;
/**
- * @author cmautner@google.com (Craig Mautner)
* Singleton class that carries out the animations and Surface operations in a separate task
* on behalf of WindowManagerService.
*/
@@ -67,6 +69,9 @@
int mBulkUpdateParams = 0;
+ DimAnimator mDimAnimator = null;
+ DimAnimator.Parameters mDimParams = null;
+
WindowAnimator(final WindowManagerService service, final Context context,
final WindowManagerPolicy policy) {
mService = service;
@@ -91,7 +96,8 @@
if (mService.mWallpaperTarget == target
|| mService.mLowerWallpaperTarget == target
|| mService.mUpperWallpaperTarget == target) {
- for (int i=0; i<mService.mWindows.size(); i++) {
+ final int N = mService.mWindows.size();
+ for (int i = 0; i < N; i++) {
WindowState w = mService.mWindows.get(i);
if (w.mIsWallpaper) {
target = w;
@@ -116,32 +122,34 @@
int i;
final int NAT = mService.mAppTokens.size();
for (i=0; i<NAT; i++) {
- final AppWindowToken appToken = mService.mAppTokens.get(i);
- final boolean wasAnimating = appToken.animation != null
- && appToken.animation != WindowManagerService.sDummyAnimation;
- if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+ final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+ final boolean wasAnimating = appAnimator.animation != null
+ && appAnimator.animation != WindowManagerService.sDummyAnimation;
+ if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appToken + " done");
+ mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
+ mPendingLayoutChanges);
}
}
}
final int NEAT = mService.mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
- final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
- final boolean wasAnimating = appToken.animation != null
- && appToken.animation != WindowManagerService.sDummyAnimation;
- if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+ final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+ final boolean wasAnimating = appAnimator.animation != null
+ && appAnimator.animation != WindowManagerService.sDummyAnimation;
+ if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appToken + " done");
+ mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
+ + " done", mPendingLayoutChanges);
}
}
}
@@ -185,13 +193,13 @@
&& winAnimator.mAnimation.getDetachWallpaper()) {
mDetachedWallpaper = w;
}
- if (winAnimator.mAnimation.getBackgroundColor() != 0) {
+ final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
+ if (backgroundColor != 0) {
if (mWindowAnimationBackground == null
|| (winAnimator.mAnimLayer <
mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
mWindowAnimationBackground = w;
- mWindowAnimationBackgroundColor =
- winAnimator.mAnimation.getBackgroundColor();
+ mWindowAnimationBackgroundColor = backgroundColor;
}
}
}
@@ -201,19 +209,21 @@
// If this window's app token is running a detached wallpaper
// animation, make a note so we can ensure the wallpaper is
// displayed behind it.
- if (w.mAppToken != null && w.mAppToken.animation != null
- && w.mAppToken.animating) {
+ final AppWindowAnimator appAnimator =
+ w.mAppToken == null ? null : w.mAppToken.mAppAnimator;
+ if (appAnimator != null && appAnimator.animation != null
+ && appAnimator.animating) {
if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0
- && w.mAppToken.animation.getDetachWallpaper()) {
+ && appAnimator.animation.getDetachWallpaper()) {
mDetachedWallpaper = w;
}
- if (w.mAppToken.animation.getBackgroundColor() != 0) {
+ final int backgroundColor = appAnimator.animation.getBackgroundColor();
+ if (backgroundColor != 0) {
if (mWindowAnimationBackground == null
|| (winAnimator.mAnimLayer <
mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
mWindowAnimationBackground = w;
- mWindowAnimationBackgroundColor =
- w.mAppToken.animation.getBackgroundColor();
+ mWindowAnimationBackgroundColor = backgroundColor;
}
}
}
@@ -222,7 +232,8 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
+ mPendingLayoutChanges);
}
}
@@ -231,17 +242,18 @@
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: "
+ w);
- mService.mInnerFields.mWallpaperForceHidingChanged = true;
+ mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
+ mPendingLayoutChanges);
}
mService.mFocusMayChange = true;
} else if (w.isReadyForDisplay() && winAnimator.mAnimation == null) {
mForceHiding = true;
}
} else if (mPolicy.canBeForceHidden(w, attrs)) {
- boolean changed;
+ final boolean changed;
if (mForceHiding) {
changed = w.hideLw(false, false);
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
@@ -251,7 +263,7 @@
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
"Now policy shown: " + w);
if (changed) {
- if (mService.mInnerFields.mWallpaperForceHidingChanged
+ if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
&& w.isVisibleNow() /*w.isReadyForDisplay()*/) {
// Assume we will need to animate. If
// we don't (because the wallpaper will
@@ -275,14 +287,15 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
+ mPendingLayoutChanges);
}
}
}
}
final AppWindowToken atoken = w.mAppToken;
- if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+ if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
if (atoken.lastTransactionSequence != mTransactionSequence) {
atoken.lastTransactionSequence = mTransactionSequence;
atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
@@ -306,14 +319,14 @@
}
}
if (w != atoken.startingWindow) {
- if (!atoken.freezingScreen || !w.mAppFreezing) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
atoken.numInterestingWindows++;
if (w.isDrawnLw()) {
atoken.numDrawnWindows++;
if (WindowManagerService.DEBUG_VISIBILITY ||
WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
"tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.freezingScreen
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ " mAppFreezing=" + w.mAppFreezing);
mTokenMayBeDrawn = true;
}
@@ -326,17 +339,20 @@
if (winAnimator.performShowLocked()) {
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
+ mPendingLayoutChanges);
}
}
}
- if (atoken != null && atoken.thumbnail != null) {
- if (atoken.thumbnailTransactionSeq != mTransactionSequence) {
- atoken.thumbnailTransactionSeq = mTransactionSequence;
- atoken.thumbnailLayer = 0;
+ final AppWindowAnimator appAnimator =
+ atoken == null ? null : atoken.mAppAnimator;
+ if (appAnimator != null && appAnimator.thumbnail != null) {
+ if (appAnimator.thumbnailTransactionSeq != mTransactionSequence) {
+ appAnimator.thumbnailTransactionSeq = mTransactionSequence;
+ appAnimator.thumbnailLayer = 0;
}
- if (atoken.thumbnailLayer < winAnimator.mAnimLayer) {
- atoken.thumbnailLayer = winAnimator.mAnimLayer;
+ if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
+ appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
}
}
} // end forall windows
@@ -348,7 +364,7 @@
final int NT = mService.mAppTokens.size();
for (int i=0; i<NT; i++) {
AppWindowToken wtoken = mService.mAppTokens.get(i);
- if (wtoken.freezingScreen) {
+ if (wtoken.mAppAnimator.freezingScreen) {
int numInteresting = wtoken.numInterestingWindows;
if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -361,7 +377,6 @@
"Setting mOrientationChangeComplete=true because wtoken "
+ wtoken + " numInteresting=" + numInteresting
+ " numDrawn=" + wtoken.numDrawnWindows);
- mService.mInnerFields.mOrientationChangeComplete = true;
}
} else if (!wtoken.allDrawn) {
int numInteresting = wtoken.numInterestingWindows;
@@ -373,7 +388,8 @@
wtoken.allDrawn = true;
mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked");
+ mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
+ mPendingLayoutChanges);
}
// We can now show all of the drawn windows!
@@ -419,14 +435,13 @@
mScreenRotationAnimation.updateSurfaces();
}
- final int N = mService.mWindows.size();
- for (int i=N-1; i>=0; i--) {
+ for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
WindowState w = mService.mWindows.get(i);
w.mWinAnimator.prepareSurfaceLocked(true);
}
- if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
- mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
+ if (mDimAnimator != null && mDimAnimator.mDimShown) {
+ mAnimating |= mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
mCurrentTime, !mService.okToDisplay());
}
@@ -438,6 +453,10 @@
mService.mBlackFrame.clearMatrix();
}
}
+
+ if (mDimParams != null) {
+ mDimAnimator.updateParameters(mContext.getResources(), mDimParams, mCurrentTime);
+ }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -450,7 +469,7 @@
}
WindowState mCurrentFocus;
- void setCurrentFocus(WindowState currentFocus) {
+ void setCurrentFocus(final WindowState currentFocus) {
mCurrentFocus = currentFocus;
}
@@ -462,6 +481,20 @@
mInnerDh = appHeight;
}
+ void startDimming(final WindowStateAnimator winAnimator, final float target,
+ final int width, final int height) {
+ if (mDimAnimator == null) {
+ mDimAnimator = new DimAnimator(mService.mFxSession);
+ }
+ mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+ new DimAnimator.Parameters(winAnimator, width, height, target)));
+ }
+
+ // TODO(cmautner): Move into Handler
+ void stopDimming() {
+ mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, null));
+ }
+
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
if (mWindowDetachedWallpaper != null) {
pw.print(" mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
@@ -470,5 +503,11 @@
pw.println(" mWindowAnimationBackgroundSurface:");
mWindowAnimationBackgroundSurface.printTo(" ", pw);
}
+ if (mDimAnimator != null) {
+ pw.println(" mDimAnimator:");
+ mDimAnimator.printTo(" ", pw);
+ } else {
+ pw.println( " no DimAnimator ");
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index afbc348..7eca401 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -149,7 +149,7 @@
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
- static final boolean DEBUG_ADD_REMOVE = true;
+ static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
@@ -158,7 +158,7 @@
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
- static final boolean DEBUG_WINDOW_MOVEMENT = true;
+ static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_TOKEN_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_ORIENTATION = false;
@@ -171,7 +171,7 @@
static final boolean DEBUG_SCREEN_ON = false;
static final boolean DEBUG_SCREENSHOT = false;
static final boolean DEBUG_BOOT = false;
- static final boolean DEBUG_LAYOUT_REPEATS = false;
+ static final boolean DEBUG_LAYOUT_REPEATS = true;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -353,7 +353,32 @@
* controlling the ordering of windows in different applications. This
* contains AppWindowToken objects.
*/
- final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>() {
+ @Override
+ public void add(int index, AppWindowToken object) {
+ synchronized (mAnimator) {
+ super.add(index, object);
+ }
+ };
+ @Override
+ public boolean add(AppWindowToken object) {
+ synchronized (mAnimator) {
+ return super.add(object);
+ }
+ };
+ @Override
+ public AppWindowToken remove(int index) {
+ synchronized (mAnimator) {
+ return super.remove(index);
+ }
+ };
+ @Override
+ public boolean remove(Object object) {
+ synchronized (mAnimator) {
+ return super.remove(object);
+ }
+ };
+ };
/**
* Application tokens that are in the process of exiting, but still
@@ -370,7 +395,32 @@
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
- final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mWindows = new ArrayList<WindowState>() {
+ @Override
+ public void add(int index, WindowState object) {
+ synchronized (mAnimator) {
+ super.add(index, object);
+ }
+ };
+ @Override
+ public boolean add(WindowState object) {
+ synchronized (mAnimator) {
+ return super.add(object);
+ }
+ };
+ @Override
+ public WindowState remove(int index) {
+ synchronized (mAnimator) {
+ return super.remove(index);
+ }
+ };
+ @Override
+ public boolean remove(Object object) {
+ synchronized (mAnimator) {
+ return super.remove(object);
+ }
+ };
+ };
/**
* Fake windows added to the window manager. Note: ordered from top to
@@ -427,7 +477,6 @@
IInputMethodManager mInputMethodManager;
SurfaceSession mFxSession;
- DimAnimator mDimAnimator = null;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
@@ -590,8 +639,10 @@
/** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple
* methods. */
class LayoutFields {
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
+ static final int SET_UPDATE_ROTATION = 1 << 0;
+ static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
+ static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
+ static final int CLEAR_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
@@ -1214,7 +1265,7 @@
AppWindowToken token = curTarget.mAppToken;
WindowState highestTarget = null;
int highestPos = 0;
- if (token.animating || token.animation != null) {
+ if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
int pos = localmWindows.indexOf(curTarget);
while (pos >= 0) {
WindowState win = localmWindows.get(pos);
@@ -1274,7 +1325,7 @@
mInputMethodTarget = w;
mInputMethodTargetWaitingAnim = false;
if (w.mAppToken != null) {
- setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
+ setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
} else {
setInputMethodAnimLayerAdjustment(0);
}
@@ -1537,12 +1588,12 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
- ? wallpaperTarget.mAppToken.animation : null)
+ ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
+ " upper=" + mUpperWallpaperTarget
+ " lower=" + mLowerWallpaperTarget);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
- && wallpaperTarget.mAppToken.animation != null)))
+ && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
|| mUpperWallpaperTarget != null
|| mLowerWallpaperTarget != null;
}
@@ -1582,7 +1633,7 @@
if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
// If this window's app token is hidden and not animating,
// it is of no interest to us.
- if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+ if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Skipping not hidden or animating token: " + w);
continue;
@@ -1597,7 +1648,7 @@
foundW = w;
foundI = i;
if (w == mWallpaperTarget && ((w.mAppToken != null
- && w.mAppToken.animation != null)
+ && w.mAppToken.mAppAnimator.animation != null)
|| w.mWinAnimator.mAnimation != null)) {
// The current wallpaper target is animating, so we'll
// look behind it for another possible target and figure
@@ -1656,9 +1707,11 @@
// animating, then we are in our super special mode!
if (foundW != null && oldW != null) {
boolean oldAnim = oldW.mWinAnimator.mAnimation != null
- || (oldW.mAppToken != null && oldW.mAppToken.animation != null);
+ || (oldW.mAppToken != null
+ && oldW.mAppToken.mAppAnimator.animation != null);
boolean foundAnim = foundW.mWinAnimator.mAnimation != null
- || (foundW.mAppToken != null && foundW.mAppToken.animation != null);
+ || (foundW.mAppToken != null &&
+ foundW.mAppToken.mAppAnimator.animation != null);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "New animation: " + foundAnim
+ " old animation: " + oldAnim);
@@ -1711,10 +1764,10 @@
// Is it time to stop animating?
boolean lowerAnimating = mLowerWallpaperTarget.mWinAnimator.mAnimation != null
|| (mLowerWallpaperTarget.mAppToken != null
- && mLowerWallpaperTarget.mAppToken.animation != null);
+ && mLowerWallpaperTarget.mAppToken.mAppAnimator.animation != null);
boolean upperAnimating = mUpperWallpaperTarget.mWinAnimator.mAnimation != null
|| (mUpperWallpaperTarget.mAppToken != null
- && mUpperWallpaperTarget.mAppToken.animation != null);
+ && mUpperWallpaperTarget.mAppToken.mAppAnimator.animation != null);
if (!lowerAnimating || !upperAnimating) {
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "No longer animating wallpaper targets!");
@@ -1736,7 +1789,7 @@
// between two wallpaper targets.
mWallpaperAnimLayerAdjustment =
(mLowerWallpaperTarget == null && foundW.mAppToken != null)
- ? foundW.mAppToken.animLayerAdjustment : 0;
+ ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0;
final int maxLayer = mPolicy.getMaxWallpaperLayer()
* TYPE_LAYER_MULTIPLIER
@@ -2007,6 +2060,7 @@
winAnimator.computeShownFrameLocked();
// No need to lay out the windows - we can just set the wallpaper position
// directly.
+ // TODO(cmautner): Don't move this from here, just lock the WindowAnimator.
if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left
|| winAnimator.mSurfaceY != wallpaper.mShownFrame.top) {
Surface.openTransaction();
@@ -2306,7 +2360,7 @@
+ " mExiting=" + win.mExiting
+ " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
- + (win.mAppToken != null ? win.mAppToken.animation : null)
+ + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -3174,13 +3228,13 @@
}
Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
}
- wtoken.setAnimation(a, initialized);
+ wtoken.mAppAnimator.setAnimation(a, initialized);
}
} else {
- wtoken.clearAnimation();
+ wtoken.mAppAnimator.clearAnimation();
}
- return wtoken.animation != null;
+ return wtoken.mAppAnimator.animation != null;
}
// -------------------------------------------------------------
@@ -3325,7 +3379,7 @@
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
-
+
// Get the dispatching timeout here while we are not holding any locks so that it
// can be cached by the AppWindowToken. The timeout value is used later by the
// input dispatcher in code that does hold locks. If we did not cache the value
@@ -3828,14 +3882,16 @@
wtoken.clientHidden = ttoken.clientHidden;
wtoken.sendAppVisibilityToClients();
}
- if (ttoken.animation != null) {
- wtoken.animation = ttoken.animation;
- wtoken.animating = ttoken.animating;
- wtoken.animLayerAdjustment = ttoken.animLayerAdjustment;
- ttoken.animation = null;
- ttoken.animLayerAdjustment = 0;
- wtoken.updateLayers();
- ttoken.updateLayers();
+ final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+ final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+ if (tAppAnimator.animation != null) {
+ wAppAnimator.animation = tAppAnimator.animation;
+ wAppAnimator.animating = tAppAnimator.animating;
+ wAppAnimator.animLayerAdjustment = tAppAnimator.animLayerAdjustment;
+ tAppAnimator.animation = null;
+ tAppAnimator.animLayerAdjustment = 0;
+ wAppAnimator.updateLayers();
+ tAppAnimator.updateLayers();
}
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -3860,18 +3916,20 @@
mH.sendMessageAtFrontOfQueue(m);
return;
}
- if (ttoken.thumbnail != null) {
+ final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+ final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+ if (tAppAnimator.thumbnail != null) {
// The old token is animating with a thumbnail, transfer
// that to the new token.
- if (wtoken.thumbnail != null) {
- wtoken.thumbnail.destroy();
+ if (wAppAnimator.thumbnail != null) {
+ wAppAnimator.thumbnail.destroy();
}
- wtoken.thumbnail = ttoken.thumbnail;
- wtoken.thumbnailX = ttoken.thumbnailX;
- wtoken.thumbnailY = ttoken.thumbnailY;
- wtoken.thumbnailLayer = ttoken.thumbnailLayer;
- wtoken.thumbnailAnimation = ttoken.thumbnailAnimation;
- ttoken.thumbnail = null;
+ wAppAnimator.thumbnail = tAppAnimator.thumbnail;
+ wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
+ wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
+ wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
+ wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
+ tAppAnimator.thumbnail = null;
}
}
}
@@ -3961,12 +4019,12 @@
boolean runningAppAnimation = false;
if (transit != WindowManagerPolicy.TRANSIT_UNSET) {
- if (wtoken.animation == sDummyAnimation) {
- wtoken.animation = null;
+ if (wtoken.mAppAnimator.animation == sDummyAnimation) {
+ wtoken.mAppAnimator.animation = null;
}
applyAnimationLocked(wtoken, lp, transit, visible);
changed = true;
- if (wtoken.animation != null) {
+ if (wtoken.mAppAnimator.animation != null) {
delayed = runningAppAnimation = true;
}
}
@@ -4029,7 +4087,7 @@
}
}
- if (wtoken.animation != null) {
+ if (wtoken.mAppAnimator.animation != null) {
delayed = true;
}
@@ -4074,7 +4132,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Setting dummy animation on: " + wtoken);
- wtoken.setDummyAnimation();
+ wtoken.mAppAnimator.setDummyAnimation();
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
wtoken.waitingToShow = wtoken.waitingToHide = false;
@@ -4124,7 +4182,7 @@
void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
boolean unfreezeSurfaceNow, boolean force) {
- if (wtoken.freezingScreen) {
+ if (wtoken.mAppAnimator.freezingScreen) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + wtoken
+ " force=" + force);
final int N = wtoken.allAppWindows.size();
@@ -4142,7 +4200,7 @@
}
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
- wtoken.freezingScreen = false;
+ wtoken.mAppAnimator.freezingScreen = false;
mAppsFreezingScreen--;
}
if (unfreezeSurfaceNow) {
@@ -4165,11 +4223,11 @@
}
Slog.i(TAG, "Set freezing of " + wtoken.appToken
+ ": hidden=" + wtoken.hidden + " freezing="
- + wtoken.freezingScreen, e);
+ + wtoken.mAppAnimator.freezingScreen, e);
}
if (!wtoken.hiddenRequested) {
- if (!wtoken.freezingScreen) {
- wtoken.freezingScreen = true;
+ if (!wtoken.mAppAnimator.freezingScreen) {
+ wtoken.mAppAnimator.freezingScreen = true;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
startFreezingDisplayLocked(false);
@@ -4222,7 +4280,7 @@
}
final long origId = Binder.clearCallingIdentity();
if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + token
- + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen);
+ + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen);
unsetAppFreezingScreenLocked(wtoken, true, force);
Binder.restoreCallingIdentity(origId);
}
@@ -4257,8 +4315,8 @@
}
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Removing app " + wtoken + " delayed=" + delayed
- + " animation=" + wtoken.animation
- + " animating=" + wtoken.animating);
+ + " animation=" + wtoken.mAppAnimator.animation
+ + " animating=" + wtoken.mAppAnimator.animating);
if (delayed) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -4268,9 +4326,8 @@
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
- wtoken.clearAnimation();
- wtoken.animation = null;
- wtoken.animating = false;
+ wtoken.mAppAnimator.clearAnimation();
+ wtoken.mAppAnimator.animating = false;
}
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken: " + wtoken);
@@ -6657,6 +6714,7 @@
public static final int ANIMATOR_WHAT_OFFSET = 100000;
public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2;
+ public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3;
private Session mLastReportedHold;
@@ -6980,14 +7038,16 @@
case APP_FREEZE_TIMEOUT: {
synchronized (mWindowMap) {
- Slog.w(TAG, "App freeze timeout expired.");
- int i = mAppTokens.size();
- while (i > 0) {
- i--;
- AppWindowToken tok = mAppTokens.get(i);
- if (tok.freezingScreen) {
- Slog.w(TAG, "Force clearing freeze: " + tok);
- unsetAppFreezingScreenLocked(tok, true, true);
+ synchronized (mAnimator) {
+ Slog.w(TAG, "App freeze timeout expired.");
+ int i = mAppTokens.size();
+ while (i > 0) {
+ i--;
+ AppWindowToken tok = mAppTokens.get(i);
+ if (tok.mAppAnimator.freezingScreen) {
+ Slog.w(TAG, "Force clearing freeze: " + tok);
+ unsetAppFreezingScreenLocked(tok, true, true);
+ }
}
}
}
@@ -7069,6 +7129,7 @@
}
case BULK_UPDATE_PARAMETERS: {
+ // Used to send multiple changes from the animation side to the layout side.
synchronized (mWindowMap) {
// TODO(cmautner): As the number of bits grows, use masks of bit groups to
// eliminate unnecessary tests.
@@ -7078,6 +7139,12 @@
if ((msg.arg1 & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
mInnerFields.mWallpaperMayChange = true;
}
+ if ((msg.arg1 & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
+ mInnerFields.mWallpaperForceHidingChanged = true;
+ }
+ if ((msg.arg1 & LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE) != 0) {
+ mInnerFields.mOrientationChangeComplete = false;
+ }
requestTraversalLocked();
}
@@ -7087,21 +7154,34 @@
// Animation messages. Move to Window{State}Animator
case SET_TRANSPARENT_REGION: {
// TODO(cmautner): Remove sync.
- synchronized (mWindowMap) {
+ synchronized (mAnimator) {
Pair<WindowStateAnimator, Region> pair =
(Pair<WindowStateAnimator, Region>) msg.obj;
final WindowStateAnimator winAnimator = pair.first;
winAnimator.setTransparentRegionHint(pair.second);
}
+
+ scheduleAnimationLocked();
break;
}
case SET_WALLPAPER_OFFSET: {
// TODO(cmautner): Remove sync.
- synchronized (mWindowMap) {
+ synchronized (mAnimator) {
final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj;
winAnimator.setWallpaperOffset(msg.arg1, msg.arg2);
}
+
+ scheduleAnimationLocked();
+ break;
+ }
+
+ case SET_DIM_PARAMETERS: {
+ synchronized (mAnimator) {
+ mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj;
+ }
+
+ scheduleAnimationLocked();
break;
}
}
@@ -7450,9 +7530,11 @@
w.mLayer = curLayer;
}
if (w.mTargetAppToken != null) {
- w.mWinAnimator.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
+ w.mWinAnimator.mAnimLayer =
+ w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
} else if (w.mAppToken != null) {
- w.mWinAnimator.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
+ w.mWinAnimator.mAnimLayer =
+ w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;
} else {
w.mWinAnimator.mAnimLayer = w.mLayer;
}
@@ -7886,10 +7968,10 @@
AppWindowToken wtoken = mOpeningApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now opening app" + wtoken);
- wtoken.clearThumbnail();
+ wtoken.mAppAnimator.clearThumbnail();
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
- wtoken.animation = null;
+ wtoken.mAppAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, true,
transit, false);
wtoken.updateReportedVisibilityLocked();
@@ -7914,9 +7996,9 @@
AppWindowToken wtoken = mClosingApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now closing app" + wtoken);
- wtoken.clearThumbnail();
+ wtoken.mAppAnimator.clearThumbnail();
wtoken.inPendingTransaction = false;
- wtoken.animation = null;
+ wtoken.mAppAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, false,
transit, false);
wtoken.updateReportedVisibilityLocked();
@@ -7928,7 +8010,7 @@
}
if (mNextAppTransitionThumbnail != null && topOpeningApp != null
- && topOpeningApp.animation != null) {
+ && topOpeningApp.mAppAnimator.animation != null) {
// This thumbnail animation is very special, we need to have
// an extra surface with the thumbnail included with the animation.
Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
@@ -7937,7 +8019,7 @@
Surface surface = new Surface(mFxSession, Process.myPid(),
"thumbnail anim", 0, dirty.width(), dirty.height(),
PixelFormat.TRANSLUCENT, Surface.HIDDEN);
- topOpeningApp.thumbnail = surface;
+ topOpeningApp.mAppAnimator.thumbnail = surface;
if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
+ surface + ": CREATE");
Surface drawSurface = new Surface();
@@ -7946,17 +8028,17 @@
c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null);
drawSurface.unlockCanvasAndPost(c);
drawSurface.release();
- topOpeningApp.thumbnailLayer = topOpeningLayer;
+ topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
Animation anim = createThumbnailAnimationLocked(transit, true, true);
- topOpeningApp.thumbnailAnimation = anim;
+ topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
- topOpeningApp.thumbnailX = mNextAppTransitionStartX;
- topOpeningApp.thumbnailY = mNextAppTransitionStartY;
+ topOpeningApp.mAppAnimator.thumbnailX = mNextAppTransitionStartX;
+ topOpeningApp.mAppAnimator.thumbnailY = mNextAppTransitionStartY;
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width()
+ " h=" + dirty.height(), e);
- topOpeningApp.clearThumbnail();
+ topOpeningApp.mAppAnimator.clearThumbnail();
}
}
@@ -8038,7 +8120,6 @@
}
}
mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
- mInnerFields.mWallpaperForceHidingChanged = false;
if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
+ " NEW: " + mWallpaperTarget
+ " LOWER: " + mLowerWallpaperTarget);
@@ -8177,16 +8258,16 @@
if (!mInnerFields.mDimming) {
//Slog.i(TAG, "DIM BEHIND: " + w);
mInnerFields.mDimming = true;
- if (mDimAnimator == null) {
- mDimAnimator = new DimAnimator(mFxSession);
- }
+ final int width, height;
if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
- mDimAnimator.show(mCurDisplayWidth, mCurDisplayHeight);
+ width = mCurDisplayWidth;
+ height = mCurDisplayHeight;
} else {
- mDimAnimator.show(innerDw, innerDh);
+ width = innerDw;
+ height = innerDh;
}
- mDimAnimator.updateParameters(mContext.getResources(),
- w, currentTime);
+ mAnimator.startDimming(w.mWinAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
+ width, height);
}
}
}
@@ -8223,7 +8304,6 @@
mExitingAppTokens.get(i).hasVisible = false;
}
- mInnerFields.mOrientationChangeComplete = true;
mInnerFields.mHoldScreen = null;
mInnerFields.mScreenBrightness = -1;
mInnerFields.mButtonBrightness = -1;
@@ -8252,7 +8332,6 @@
}
try {
- mInnerFields.mWallpaperForceHidingChanged = false;
int repeats = 0;
do {
@@ -8263,7 +8342,8 @@
break;
}
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ mPendingLayoutChanges);
if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
@@ -8294,7 +8374,8 @@
// FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
// it is animating.
mPendingLayoutChanges = 0;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount);
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
+ mPendingLayoutChanges);
mPolicy.beginAnimationLw(dw, dh);
for (i = mWindows.size() - 1; i >= 0; i--) {
WindowState w = mWindows.get(i);
@@ -8303,7 +8384,8 @@
}
}
mPendingLayoutChanges |= mPolicy.finishAnimationLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
+ mPendingLayoutChanges);
} while (mPendingLayoutChanges != 0);
final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -8337,6 +8419,9 @@
updateWallpaperVisibilityLocked();
}
}
+ if (!mInnerFields.mDimming) {
+ mAnimator.stopDimming();
+ }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -8348,7 +8433,8 @@
// to go.
if (mAppTransitionReady) {
mPendingLayoutChanges |= handleAppTransitionReadyLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
+ mPendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8361,7 +8447,8 @@
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
+ mPendingLayoutChanges);
}
if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
@@ -8373,9 +8460,10 @@
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
mPendingLayoutChanges |= animateAwayWallpaperLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
+ mPendingLayoutChanges);
}
-
+ mInnerFields.mWallpaperForceHidingChanged = false;
if (mInnerFields.mWallpaperMayChange) {
if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
@@ -8405,7 +8493,7 @@
if (mLayoutNeeded) {
mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
}
final int N = mWindows.size();
@@ -8426,7 +8514,8 @@
mInnerFields.mWallpaperMayChange = true;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("updateWindowsAndWallpaperLocked 1");
+ debugLayoutRepeats("updateWindowsAndWallpaperLocked 1",
+ mPendingLayoutChanges);
}
}
}
@@ -8455,7 +8544,7 @@
// associated with exiting/removed apps
mAnimator.animate();
mPendingLayoutChanges |= mAnimator.mPendingLayoutChanges;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()", mPendingLayoutChanges);
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
@@ -8550,9 +8639,8 @@
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
- token.clearAnimation();
- token.animation = null;
- token.animating = false;
+ token.mAppAnimator.clearAnimation();
+ token.mAppAnimator.animating = false;
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"performLayout: App token exiting now removed" + token);
mAppTokens.remove(token);
@@ -8629,6 +8717,7 @@
!mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ mInnerFields.mOrientationChangeComplete = true;
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -8822,7 +8911,7 @@
TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
- mAnimator.setCurrentFocus(mCurrentFocus);
+ mAnimator.setCurrentFocus(newFocus);
mLosingFocus.remove(newFocus);
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
@@ -8869,12 +8958,11 @@
WindowState result = null;
WindowState win;
- int i = mWindows.size() - 1;
int nextAppIndex = mAppTokens.size()-1;
WindowToken nextApp = nextAppIndex >= 0
? mAppTokens.get(nextAppIndex) : null;
- while (i >= 0) {
+ for (int i = mWindows.size() - 1; i >= 0; i--) {
win = mWindows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
@@ -8887,7 +8975,6 @@
// If this window's application has been removed, just skip it.
if (thisApp != null && thisApp.removed) {
- i--;
continue;
}
@@ -8927,8 +9014,6 @@
result = win;
break;
}
-
- i--;
}
return result;
@@ -9476,12 +9561,6 @@
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
- if (mDimAnimator != null) {
- pw.println(" mDimAnimator:");
- mDimAnimator.printTo(" ", pw);
- } else {
- pw.println( " no DimAnimator ");
- }
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
@@ -9691,10 +9770,10 @@
requestTraversalLocked();
}
- void debugLayoutRepeats(final String msg) {
+ void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
- Slog.v(TAG, "Layouts looping: " + msg);
- Slog.v(TAG, "mPendingLayoutChanges = 0x" + Integer.toHexString(mPendingLayoutChanges));
+ Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" +
+ Integer.toHexString(pendingLayoutChanges));
}
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index b74aa61..05797a4 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -65,6 +65,9 @@
WindowToken mRootToken;
AppWindowToken mAppToken;
AppWindowToken mTargetAppToken;
+
+ // mAttrs.flags is tested in animation without being locked. If the bits tested are ever
+ // modified they will need to be locked.
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mAttachedWindow;
@@ -621,7 +624,7 @@
}
final AppWindowToken atoken = mAppToken;
final boolean animating = atoken != null
- ? (atoken.animation != null) : false;
+ ? (atoken.mAppAnimator.animation != null) : false;
return mHasSurface && !mDestroying && !mExiting
&& (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
@@ -637,7 +640,7 @@
public boolean isWinVisibleLw() {
final AppWindowToken atoken = mAppToken;
return mHasSurface && mPolicyVisibility && !mAttachedHidden
- && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+ && (atoken == null || !atoken.hiddenRequested || atoken.mAppAnimator.animating)
&& !mExiting && !mDestroying;
}
@@ -685,7 +688,7 @@
final AppWindowToken atoken = mAppToken;
if (atoken != null) {
return ((!mAttachedHidden && !atoken.hiddenRequested)
- || mWinAnimator.mAnimation != null || atoken.animation != null);
+ || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
}
return !mAttachedHidden || mWinAnimator.mAnimation != null;
}
@@ -703,7 +706,7 @@
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
&& !mRootToken.hidden)
|| mWinAnimator.mAnimation != null
- || ((mAppToken != null) && (mAppToken.animation != null)));
+ || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
}
/**
@@ -746,7 +749,7 @@
return (mAttrs.format == PixelFormat.OPAQUE
|| mAttrs.type == TYPE_WALLPAPER)
&& isDrawnLw() && mWinAnimator.mAnimation == null
- && (mAppToken == null || mAppToken.animation == null);
+ && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
}
/**
@@ -962,8 +965,9 @@
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
pw.print(" mSubLayer="); pw.print(mSubLayer);
pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
- pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
- : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
+ pw.print((mTargetAppToken != null ?
+ mTargetAppToken.mAppAnimator.animLayerAdjustment
+ : (mAppToken != null ? mAppToken.mAppAnimator.animLayerAdjustment : 0)));
pw.print("="); pw.print(mWinAnimator.mAnimLayer);
pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer);
}
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 4979a4c..6d0921e 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -5,6 +5,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE;
+
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
@@ -160,7 +162,7 @@
return mAnimation != null
|| (attached != null && attached.mWinAnimator.mAnimation != null)
|| (atoken != null &&
- (atoken.animation != null
+ (atoken.mAppAnimator.animation != null
|| atoken.inPendingTransaction));
}
@@ -226,7 +228,7 @@
}
mHasLocalTransformation = false;
if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
- && mWin.mAppToken.animation != null) {
+ && mWin.mAppToken.mAppAnimator.animation != null) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
@@ -313,8 +315,9 @@
}
finishExit();
- mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
+ mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -613,9 +616,10 @@
Transformation attachedTransformation =
(mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
? mAttachedWindow.mWinAnimator.mTransformation : null;
- Transformation appTransformation =
- (mWin.mAppToken != null && mWin.mAppToken.hasTransformation)
- ? mWin.mAppToken.transformation : null;
+ final AppWindowAnimator appAnimator =
+ mWin.mAppToken == null ? null : mWin.mAppToken.mAppAnimator;
+ Transformation appTransformation = (appAnimator != null && appAnimator.hasTransformation)
+ ? appAnimator.transformation : null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
@@ -629,11 +633,13 @@
Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
}
}
- if (mService.mWallpaperTarget.mAppToken != null &&
- mService.mWallpaperTarget.mAppToken.hasTransformation &&
- mService.mWallpaperTarget.mAppToken.animation != null &&
- !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
- appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
+ final AppWindowAnimator wpAppAnimator = mService.mWallpaperTarget.mAppToken == null
+ ? null : mService.mWallpaperTarget.mAppToken.mAppAnimator;
+ if (wpAppAnimator != null &&
+ wpAppAnimator.hasTransformation &&
+ wpAppAnimator.animation != null &&
+ !wpAppAnimator.animation.getDetachWallpaper()) {
+ appTransformation = wpAppAnimator.transformation;
if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
Slog.v(TAG, "WP target app xform: " + appTransformation);
}
@@ -916,7 +922,7 @@
if (displayed) {
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
- mService.mInnerFields.mOrientationChangeComplete = false;
+ mAnimator.mBulkUpdateParams |= CLEAR_ORIENTATION_CHANGE_COMPLETE;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
@@ -981,7 +987,7 @@
+ (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
+ " animating=" + mAnimating
+ " tok animating="
- + (mWin.mAppToken != null ? mWin.mAppToken.animating : false));
+ + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false));
if (!showSurfaceRobustlyLocked()) {
return false;
}
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index fc9832c..30876d0 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -28,6 +28,7 @@
import junit.framework.TestSuite;
import junit.runner.BaseTestRunner;
+import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@@ -91,15 +92,35 @@
private TestCase buildSingleTestMethod(Class testClass, String testMethodName) {
try {
- TestCase testCase = (TestCase) testClass.newInstance();
+ Constructor c = testClass.getConstructor();
+ return newSingleTestMethod(testClass, testMethodName, c);
+ } catch (NoSuchMethodException e) {
+ }
+
+ try {
+ Constructor c = testClass.getConstructor(String.class);
+ return newSingleTestMethod(testClass, testMethodName, c, testMethodName);
+ } catch (NoSuchMethodException e) {
+ }
+
+ return null;
+ }
+
+ private TestCase newSingleTestMethod(Class testClass, String testMethodName,
+ Constructor constructor, Object... args) {
+ try {
+ TestCase testCase = (TestCase) constructor.newInstance(args);
testCase.setName(testMethodName);
return testCase;
} catch (IllegalAccessException e) {
runFailed("Could not access test class. Class: " + testClass.getName());
} catch (InstantiationException e) {
runFailed("Could not instantiate test class. Class: " + testClass.getName());
+ } catch (IllegalArgumentException e) {
+ runFailed("Illegal argument passed to constructor. Class: " + testClass.getName());
+ } catch (InvocationTargetException e) {
+ runFailed("Constructor thew an exception. Class: " + testClass.getName());
}
-
return null;
}
diff --git a/tests/BiDiTests/res/layout/textview_alignment_ltr.xml b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
new file mode 100644
index 0000000..0e1adba
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textview_alignment_ltr"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layoutDirection="ltr">
+
+ <TableLayout android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="gravity"
+ android:gravity="center_horizontal">
+
+ <TextView android:text="inherit gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="center">
+
+ <TextView android:text="inherit gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textStart">
+
+ <TextView android:text="inherit textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textEnd">
+
+ <TextView android:text="inherit textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewStart">
+
+ <TextView android:text="inherit viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewEnd">
+
+ <TextView android:text="inherit viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/textview_alignment_rtl.xml b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
new file mode 100644
index 0000000..12a90d5
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textview_alignment_rtl"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layoutDirection="rtl">
+
+ <TableLayout android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="gravity"
+ android:gravity="center_horizontal">
+
+ <TextView android:text="inherit gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="center">
+
+ <TextView android:text="inherit gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textStart">
+
+ <TextView android:text="inherit textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textEnd">
+
+ <TextView android:text="inherit textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewStart">
+
+ <TextView android:text="inherit viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewEnd">
+
+ <TextView android:text="inherit viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index c5a1235..209597e 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -104,6 +104,16 @@
addItem(result, "Canvas", BiDiTestCanvas.class, R.id.canvas);
addItem(result, "Canvas2", BiDiTestCanvas2.class, R.id.canvas2);
+ addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
+ addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
+ addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
+
+ addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
+ addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
+
+ addItem(result, "TextAlignment LTR", BiDiTestTextViewAlignmentLtr.class, R.id.textview_alignment_ltr);
+ addItem(result, "TextAlignment RTL", BiDiTestTextViewAlignmentRtl.class, R.id.textview_alignment_rtl);
+
addItem(result, "Linear LTR", BiDiTestLinearLayoutLtr.class, R.id.linear_layout_ltr);
addItem(result, "Linear RTL", BiDiTestLinearLayoutRtl.class, R.id.linear_layout_rtl);
addItem(result, "Linear LOC", BiDiTestLinearLayoutLocale.class, R.id.linear_layout_locale);
@@ -134,15 +144,9 @@
addItem(result, "Margin MIXED", BiDiTestViewGroupMarginMixed.class, R.id.view_group_margin_mixed);
- addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
- addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
- addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
-
- addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
- addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
-
addItem(result, "TextView Drawables LTR", BiDiTestTextViewDrawablesLtr.class, R.id.textview_drawables_ltr);
addItem(result, "TextView Drawables RTL", BiDiTestTextViewDrawablesRtl.class, R.id.textview_drawables_rtl);
+
addItem(result, "Gallery LTR", BiDiTestGalleryLtr.class, R.id.gallery_ltr);
addItem(result, "Gallery RTL", BiDiTestGalleryRtl.class, R.id.gallery_rtl);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
new file mode 100644
index 0000000..5ea5d81
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentLtr extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.textview_alignment_ltr, container, false);
+ }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
new file mode 100644
index 0000000..fcc7a5d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentRtl extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.textview_alignment_rtl, container, false);
+ }
+}
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 2b9b056..9de685a 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -57,6 +57,13 @@
bool is9Patch;
Res_png_9patch info9Patch;
+ // Layout padding, if relevant
+ bool haveLayoutBounds;
+ int32_t layoutBoundsLeft;
+ int32_t layoutBoundsTop;
+ int32_t layoutBoundsRight;
+ int32_t layoutBoundsBottom;
+
png_uint_32 allocHeight;
png_bytepp allocRows;
};
@@ -129,33 +136,62 @@
&interlace_type, &compression_type, NULL);
}
-static bool is_tick(png_bytep p, bool transparent, const char** outError)
+#define COLOR_TRANSPARENT 0
+#define COLOR_WHITE 0xFFFFFFFF
+#define COLOR_TICK 0xFF000000
+#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
+
+enum {
+ TICK_TYPE_NONE,
+ TICK_TYPE_TICK,
+ TICK_TYPE_LAYOUT_BOUNDS,
+ TICK_TYPE_BOTH
+};
+
+static int tick_type(png_bytep p, bool transparent, const char** outError)
{
+ png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
if (transparent) {
if (p[3] == 0) {
- return false;
+ return TICK_TYPE_NONE;
}
+ if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+ return TICK_TYPE_LAYOUT_BOUNDS;
+ }
+ if (color == COLOR_TICK) {
+ return TICK_TYPE_TICK;
+ }
+
+ // Error cases
if (p[3] != 0xff) {
*outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
- return false;
+ return TICK_TYPE_NONE;
}
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in transparent frame must be black";
+ *outError = "Ticks in transparent frame must be black or red";
}
- return true;
+ return TICK_TYPE_TICK;
}
if (p[3] != 0xFF) {
*outError = "White frame must be a solid color (no alpha)";
}
- if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
- return false;
+ if (color == COLOR_WHITE) {
+ return TICK_TYPE_NONE;
}
+ if (color == COLOR_TICK) {
+ return TICK_TYPE_TICK;
+ }
+ if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+ return TICK_TYPE_LAYOUT_BOUNDS;
+ }
+
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in white frame must be black";
- return false;
+ *outError = "Ticks in white frame must be black or red";
+ return TICK_TYPE_NONE;
}
- return true;
+ return TICK_TYPE_TICK;
}
enum {
@@ -175,7 +211,7 @@
bool found = false;
for (i=1; i<width-1; i++) {
- if (is_tick(row+i*4, transparent, outError)) {
+ if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
if (state == TICK_START ||
(state == TICK_OUTSIDE_1 && multipleAllowed)) {
*outLeft = i-1;
@@ -224,7 +260,7 @@
bool found = false;
for (i=1; i<height-1; i++) {
- if (is_tick(rows[i]+offset, transparent, outError)) {
+ if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
if (state == TICK_START ||
(state == TICK_OUTSIDE_1 && multipleAllowed)) {
*outTop = i-1;
@@ -262,6 +298,83 @@
return NO_ERROR;
}
+static status_t get_horizontal_layout_bounds_ticks(
+ png_bytep row, int width, bool transparent, bool required,
+ int32_t* outLeft, int32_t* outRight, const char** outError)
+{
+ int i;
+ *outLeft = *outRight = 0;
+
+ // Look for left tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
+ // Starting with a layout padding tick
+ i = 1;
+ while (i < width - 1) {
+ (*outLeft)++;
+ i++;
+ int tick = tick_type(row + i * 4, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ // Look for right tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
+ // Ending with a layout padding tick
+ i = width - 2;
+ while (i > 1) {
+ (*outRight)++;
+ i--;
+ int tick = tick_type(row+i*4, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+static status_t get_vertical_layout_bounds_ticks(
+ png_bytepp rows, int offset, int height, bool transparent, bool required,
+ int32_t* outTop, int32_t* outBottom, const char** outError)
+{
+ int i;
+ *outTop = *outBottom = 0;
+
+ // Look for top tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
+ // Starting with a layout padding tick
+ i = 1;
+ while (i < height - 1) {
+ (*outTop)++;
+ i++;
+ int tick = tick_type(rows[i] + offset, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ // Look for bottom tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
+ // Ending with a layout padding tick
+ i = height - 2;
+ while (i > 1) {
+ (*outBottom)++;
+ i--;
+ int tick = tick_type(rows[i] + offset, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+
static uint32_t get_color(
png_bytepp rows, int left, int top, int right, int bottom)
{
@@ -353,6 +466,9 @@
image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+ image->layoutBoundsLeft = image->layoutBoundsRight =
+ image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+
png_bytep p = image->rows[0];
bool transparent = p[3] == 0;
bool hasColor = false;
@@ -408,6 +524,25 @@
goto getout;
}
+ // Find left and right of layout padding...
+ get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
+ &image->layoutBoundsLeft,
+ &image->layoutBoundsRight, &errorMsg);
+
+ get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
+ &image->layoutBoundsTop,
+ &image->layoutBoundsBottom, &errorMsg);
+
+ image->haveLayoutBounds = image->layoutBoundsLeft != 0
+ || image->layoutBoundsRight != 0
+ || image->layoutBoundsTop != 0
+ || image->layoutBoundsBottom != 0;
+
+ if (image->haveLayoutBounds) {
+ NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
+ image->layoutBoundsRight, image->layoutBoundsBottom));
+ }
+
// Copy patch data into image
image->info9Patch.numXDivs = numXDivs;
image->info9Patch.numYDivs = numYDivs;
@@ -845,8 +980,9 @@
int bit_depth, interlace_type, compression_type;
int i;
- png_unknown_chunk unknowns[1];
+ png_unknown_chunk unknowns[2];
unknowns[0].data = NULL;
+ unknowns[1].data = NULL;
png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
if (outRows == (png_bytepp) 0) {
@@ -916,23 +1052,42 @@
}
if (imageInfo.is9Patch) {
+ int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0);
+ int p_index = imageInfo.haveLayoutBounds ? 1 : 0;
+ int b_index = 0;
+ png_byte *chunk_names = imageInfo.haveLayoutBounds
+ ? (png_byte*)"npLb\0npTc\0"
+ : (png_byte*)"npTc";
NOISY(printf("Adding 9-patch info...\n"));
- strcpy((char*)unknowns[0].name, "npTc");
- unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
- unknowns[0].size = imageInfo.info9Patch.serializedSize();
+ strcpy((char*)unknowns[p_index].name, "npTc");
+ unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize();
+ unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
// TODO: remove the check below when everything works
- checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
+ checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
+
+ if (imageInfo.haveLayoutBounds) {
+ int chunk_size = sizeof(png_uint_32) * 4;
+ strcpy((char*)unknowns[b_index].name, "npLb");
+ unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
+ memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
+ unknowns[b_index].size = chunk_size;
+ }
+
png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
- (png_byte*)"npTc", 1);
- png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
+ chunk_names, chunk_count);
+ png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
// XXX I can't get this to work without forcibly changing
// the location to what I want... which apparently is supposed
// to be a private API, but everything else I have tried results
// in the location being set to what I -last- wrote so I never
// get written. :p
png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
+ if (imageInfo.haveLayoutBounds) {
+ png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
+ }
}
+
png_write_info(write_ptr, write_info);
png_bytepp rows;
@@ -954,6 +1109,7 @@
}
free(outRows);
free(unknowns[0].data);
+ free(unknowns[1].data);
png_get_IHDR(write_ptr, write_info, &width, &height,
&bit_depth, &color_type, &interlace_type,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index eadec02..b76b8cf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -524,7 +524,8 @@
int nativeInt = sManager.addNewDelegate(delegate);
// and create/return a new Bitmap with it
- return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
+ return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/,
+ density);
}
/**
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 97afc81..a477fd1 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -453,9 +453,8 @@
public SipSessionGroupExt(SipProfile localProfile,
PendingIntent incomingCallPendingIntent,
ISipSessionListener listener) throws SipException {
- String password = localProfile.getPassword();
- SipProfile p = duplicate(localProfile);
- mSipGroup = createSipSessionGroup(mLocalIp, p, password);
+ mSipGroup = new SipSessionGroup(duplicate(localProfile),
+ localProfile.getPassword(), mTimer, mMyWakeLock);
mIncomingCallPendingIntent = incomingCallPendingIntent;
mAutoRegistration.setListener(listener);
}
@@ -478,27 +477,6 @@
mSipGroup.setWakeupTimer(timer);
}
- // network connectivity is tricky because network can be disconnected
- // at any instant so need to deal with exceptions carefully even when
- // you think you are connected
- private SipSessionGroup createSipSessionGroup(String localIp,
- SipProfile localProfile, String password) throws SipException {
- try {
- return new SipSessionGroup(localIp, localProfile, password,
- mTimer, mMyWakeLock);
- } catch (IOException e) {
- // network disconnected
- Log.w(TAG, "createSipSessionGroup(): network disconnected?");
- if (localIp != null) {
- return createSipSessionGroup(null, localProfile, password);
- } else {
- // recursive
- Log.wtf(TAG, "impossible! recursive!");
- throw new RuntimeException("createSipSessionGroup");
- }
- }
- }
-
private SipProfile duplicate(SipProfile p) {
try {
return new SipProfile.Builder(p).setPassword("*").build();
@@ -530,7 +508,7 @@
throws SipException {
mSipGroup.onConnectivityChanged();
if (connected) {
- resetGroup(mLocalIp);
+ mSipGroup.reset();
if (mOpenedToReceiveCalls) openToReceiveCalls();
} else {
// close mSipGroup but remember mOpenedToReceiveCalls
@@ -541,22 +519,6 @@
}
}
- private void resetGroup(String localIp) throws SipException {
- try {
- mSipGroup.reset(localIp);
- } catch (IOException e) {
- // network disconnected
- Log.w(TAG, "resetGroup(): network disconnected?");
- if (localIp != null) {
- resetGroup(null); // reset w/o local IP
- } else {
- // recursive
- Log.wtf(TAG, "impossible!");
- throw new RuntimeException("resetGroup");
- }
- }
- }
-
public void close() {
mOpenedToReceiveCalls = false;
mSipGroup.close();
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 877a0a4..6acd456 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -40,6 +40,7 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramSocket;
+import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.Collection;
@@ -47,13 +48,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
-import java.util.TooManyListenersException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
-import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.RequestEvent;
@@ -132,18 +131,17 @@
private int mExternalPort;
/**
- * @param myself the local profile with password crossed out
+ * @param profile the local profile with password crossed out
* @param password the password of the profile
* @throws IOException if cannot assign requested address
*/
- public SipSessionGroup(String localIp, SipProfile myself, String password,
- SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException,
- IOException {
- mLocalProfile = myself;
+ public SipSessionGroup(SipProfile profile, String password,
+ SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
+ mLocalProfile = profile;
mPassword = password;
mWakeupTimer = timer;
mWakeLock = wakeLock;
- reset(localIp);
+ reset();
}
// TODO: remove this method once SipWakeupTimer can better handle variety
@@ -152,43 +150,64 @@
mWakeupTimer = timer;
}
- synchronized void reset(String localIp) throws SipException, IOException {
- mLocalIp = localIp;
- if (localIp == null) return;
-
- SipProfile myself = mLocalProfile;
- SipFactory sipFactory = SipFactory.getInstance();
+ synchronized void reset() throws SipException {
Properties properties = new Properties();
+
+ String protocol = mLocalProfile.getProtocol();
+ int port = mLocalProfile.getPort();
+ String server = mLocalProfile.getProxyAddress();
+
+ if (!TextUtils.isEmpty(server)) {
+ properties.setProperty("javax.sip.OUTBOUND_PROXY",
+ server + ':' + port + '/' + protocol);
+ } else {
+ server = mLocalProfile.getSipDomain();
+ }
+ if (server.startsWith("[") && server.endsWith("]")) {
+ server = server.substring(1, server.length() - 1);
+ }
+
+ String local = null;
+ try {
+ for (InetAddress remote : InetAddress.getAllByName(server)) {
+ DatagramSocket socket = new DatagramSocket();
+ socket.connect(remote, port);
+ if (socket.isConnected()) {
+ local = socket.getLocalAddress().getHostAddress();
+ port = socket.getLocalPort();
+ socket.close();
+ break;
+ }
+ socket.close();
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ if (local == null) {
+ // We are unable to reach the server. Just bail out.
+ return;
+ }
+
+ close();
+ mLocalIp = local;
+
properties.setProperty("javax.sip.STACK_NAME", getStackName());
properties.setProperty(
"gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
- String outboundProxy = myself.getProxyAddress();
- if (!TextUtils.isEmpty(outboundProxy)) {
- Log.v(TAG, "outboundProxy is " + outboundProxy);
- properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
- + ":" + myself.getPort() + "/" + myself.getProtocol());
- }
- SipStack stack = mSipStack = sipFactory.createSipStack(properties);
-
+ mSipStack = SipFactory.getInstance().createSipStack(properties);
try {
- SipProvider provider = stack.createSipProvider(
- stack.createListeningPoint(localIp, allocateLocalPort(),
- myself.getProtocol()));
+ SipProvider provider = mSipStack.createSipProvider(
+ mSipStack.createListeningPoint(local, port, protocol));
provider.addSipListener(this);
- mSipHelper = new SipHelper(stack, provider);
- } catch (InvalidArgumentException e) {
- throw new IOException(e.getMessage());
- } catch (TooManyListenersException e) {
- // must never happen
- throw new SipException("SipSessionGroup constructor", e);
+ mSipHelper = new SipHelper(mSipStack, provider);
+ } catch (SipException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SipException("failed to initialize SIP stack", e);
}
- Log.d(TAG, " start stack for " + myself.getUriString());
- stack.start();
- mCallReceiverSession = null;
- mSessionMap.clear();
-
- resetExternalAddress();
+ Log.d(TAG, " start stack for " + mLocalProfile.getUriString());
+ mSipStack.start();
}
synchronized void onConnectivityChanged() {
@@ -234,6 +253,7 @@
mSipStack = null;
mSipHelper = null;
}
+ resetExternalAddress();
}
public synchronized boolean isClosed() {
@@ -257,17 +277,6 @@
return (isClosed() ? null : new SipSessionImpl(listener));
}
- private static int allocateLocalPort() throws SipException {
- try {
- DatagramSocket s = new DatagramSocket();
- int localPort = s.getLocalPort();
- s.close();
- return localPort;
- } catch (IOException e) {
- throw new SipException("allocateLocalPort()", e);
- }
- }
-
synchronized boolean containsSession(String callId) {
return mSessionMap.containsKey(callId);
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2fc6c20..c7f6bf0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -380,6 +380,7 @@
mHandler = new P2pHandler(looper);
mChannelListener = l;
}
+ private final static int INVALID_LISTENER_KEY = 0;
private ChannelListener mChannelListener;
private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
private Object mListenerMapLock = new Object();
@@ -450,16 +451,19 @@
}
int putListener(Object listener) {
- if (listener == null) return 0;
+ if (listener == null) return INVALID_LISTENER_KEY;
int key;
synchronized (mListenerMapLock) {
- key = mListenerKey++;
+ do {
+ key = mListenerKey++;
+ } while (key == INVALID_LISTENER_KEY);
mListenerMap.put(key, listener);
}
return key;
}
Object getListener(int key) {
+ if (key == INVALID_LISTENER_KEY) return null;
synchronized (mListenerMapLock) {
return mListenerMap.remove(key);
}