Merge "Notify network observers of route changes."
diff --git a/Android.mk b/Android.mk
index 24796cb..3a3756b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -705,7 +705,7 @@
$(INTERNAL_PLATFORM_API_FILE): $(full_target)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
-# ==== the private api stubs ===================================
+# ==== the system api stubs ===================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
@@ -717,11 +717,11 @@
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-LOCAL_MODULE := private-api-stubs
+LOCAL_MODULE := system-api-stubs
LOCAL_DROIDDOC_OPTIONS:=\
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_private_stubs_current_intermediates/src \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_system_stubs_current_intermediates/src \
-showAnnotation android.annotation.SystemApi \
-nodocs
diff --git a/api/current.txt b/api/current.txt
index a09a853..2f813ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7041,6 +7041,7 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String TELECOMM_SERVICE = "telecomm";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -27675,6 +27676,7 @@
field public static final java.lang.String ACTION_CALL_SERVICE;
field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
+ field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -27682,6 +27684,10 @@
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
+ field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
+ }
+
+ public class TelecommManager {
}
}
@@ -33084,7 +33090,7 @@
method public abstract boolean onTouch(android.view.View, android.view.MotionEvent);
}
- public class ViewAnimationUtils {
+ public final class ViewAnimationUtils {
method public static final android.animation.ValueAnimator createCircularReveal(android.view.View, int, int, float, float);
}
@@ -35188,12 +35194,25 @@
method public void onRequestFocus(android.webkit.WebView);
method public void onShowCustomView(android.view.View, android.webkit.WebChromeClient.CustomViewCallback);
method public deprecated void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback);
+ method public boolean showFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams);
}
public static abstract interface WebChromeClient.CustomViewCallback {
method public abstract void onCustomViewHidden();
}
+ public static class WebChromeClient.FileChooserParams {
+ ctor public WebChromeClient.FileChooserParams();
+ field public static final int MODE_OPEN_FOLDER = 2; // 0x2
+ field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1
+ field public static final int MODE_SAVE = 4; // 0x4
+ field public java.lang.String acceptTypes;
+ field public boolean capture;
+ field public java.lang.String defaultFilename;
+ field public int mode;
+ field public java.lang.String title;
+ }
+
public class WebHistoryItem implements java.lang.Cloneable {
method public android.graphics.Bitmap getFavicon();
method public java.lang.String getOriginalUrl();
@@ -37749,6 +37768,7 @@
method public float getShadowDx();
method public float getShadowDy();
method public float getShadowRadius();
+ method public final boolean getShowSoftInputOnFocus();
method public java.lang.CharSequence getText();
method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int);
method public final android.content.res.ColorStateList getTextColors();
@@ -37842,6 +37862,7 @@
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
+ method public final void setShowSoftInputOnFocus(boolean);
method public void setSingleLine();
method public void setSingleLine(boolean);
method public final void setSpannableFactory(android.text.Spannable.Factory);
@@ -40007,109 +40028,6 @@
field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES;
}
- public static final class Character.UnicodeScript extends java.lang.Enum {
- method public static java.lang.Character.UnicodeScript forName(java.lang.String);
- method public static java.lang.Character.UnicodeScript of(int);
- method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
- method public static final java.lang.Character.UnicodeScript[] values();
- enum_constant public static final java.lang.Character.UnicodeScript ARABIC;
- enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN;
- enum_constant public static final java.lang.Character.UnicodeScript AVESTAN;
- enum_constant public static final java.lang.Character.UnicodeScript BALINESE;
- enum_constant public static final java.lang.Character.UnicodeScript BAMUM;
- enum_constant public static final java.lang.Character.UnicodeScript BATAK;
- enum_constant public static final java.lang.Character.UnicodeScript BENGALI;
- enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO;
- enum_constant public static final java.lang.Character.UnicodeScript BRAHMI;
- enum_constant public static final java.lang.Character.UnicodeScript BRAILLE;
- enum_constant public static final java.lang.Character.UnicodeScript BUGINESE;
- enum_constant public static final java.lang.Character.UnicodeScript BUHID;
- enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL;
- enum_constant public static final java.lang.Character.UnicodeScript CARIAN;
- enum_constant public static final java.lang.Character.UnicodeScript CHAM;
- enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE;
- enum_constant public static final java.lang.Character.UnicodeScript COMMON;
- enum_constant public static final java.lang.Character.UnicodeScript COPTIC;
- enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM;
- enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT;
- enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC;
- enum_constant public static final java.lang.Character.UnicodeScript DESERET;
- enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI;
- enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS;
- enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC;
- enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN;
- enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC;
- enum_constant public static final java.lang.Character.UnicodeScript GOTHIC;
- enum_constant public static final java.lang.Character.UnicodeScript GREEK;
- enum_constant public static final java.lang.Character.UnicodeScript GUJARATI;
- enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI;
- enum_constant public static final java.lang.Character.UnicodeScript HAN;
- enum_constant public static final java.lang.Character.UnicodeScript HANGUL;
- enum_constant public static final java.lang.Character.UnicodeScript HANUNOO;
- enum_constant public static final java.lang.Character.UnicodeScript HEBREW;
- enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA;
- enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC;
- enum_constant public static final java.lang.Character.UnicodeScript INHERITED;
- enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI;
- enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN;
- enum_constant public static final java.lang.Character.UnicodeScript JAVANESE;
- enum_constant public static final java.lang.Character.UnicodeScript KAITHI;
- enum_constant public static final java.lang.Character.UnicodeScript KANNADA;
- enum_constant public static final java.lang.Character.UnicodeScript KATAKANA;
- enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI;
- enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI;
- enum_constant public static final java.lang.Character.UnicodeScript KHMER;
- enum_constant public static final java.lang.Character.UnicodeScript LAO;
- enum_constant public static final java.lang.Character.UnicodeScript LATIN;
- enum_constant public static final java.lang.Character.UnicodeScript LEPCHA;
- enum_constant public static final java.lang.Character.UnicodeScript LIMBU;
- enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B;
- enum_constant public static final java.lang.Character.UnicodeScript LISU;
- enum_constant public static final java.lang.Character.UnicodeScript LYCIAN;
- enum_constant public static final java.lang.Character.UnicodeScript LYDIAN;
- enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM;
- enum_constant public static final java.lang.Character.UnicodeScript MANDAIC;
- enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK;
- enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN;
- enum_constant public static final java.lang.Character.UnicodeScript MYANMAR;
- enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE;
- enum_constant public static final java.lang.Character.UnicodeScript NKO;
- enum_constant public static final java.lang.Character.UnicodeScript OGHAM;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC;
- enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI;
- enum_constant public static final java.lang.Character.UnicodeScript ORIYA;
- enum_constant public static final java.lang.Character.UnicodeScript OSMANYA;
- enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA;
- enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN;
- enum_constant public static final java.lang.Character.UnicodeScript REJANG;
- enum_constant public static final java.lang.Character.UnicodeScript RUNIC;
- enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN;
- enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA;
- enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN;
- enum_constant public static final java.lang.Character.UnicodeScript SINHALA;
- enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE;
- enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI;
- enum_constant public static final java.lang.Character.UnicodeScript SYRIAC;
- enum_constant public static final java.lang.Character.UnicodeScript TAGALOG;
- enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_LE;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET;
- enum_constant public static final java.lang.Character.UnicodeScript TAMIL;
- enum_constant public static final java.lang.Character.UnicodeScript TELUGU;
- enum_constant public static final java.lang.Character.UnicodeScript THAANA;
- enum_constant public static final java.lang.Character.UnicodeScript THAI;
- enum_constant public static final java.lang.Character.UnicodeScript TIBETAN;
- enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH;
- enum_constant public static final java.lang.Character.UnicodeScript UGARITIC;
- enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN;
- enum_constant public static final java.lang.Character.UnicodeScript VAI;
- enum_constant public static final java.lang.Character.UnicodeScript YI;
- }
-
public final class Class implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
method public java.lang.Class<? extends U> asSubclass(java.lang.Class<U>);
method public T cast(java.lang.Object);
@@ -41950,13 +41868,11 @@
method public java.lang.String getValue();
method public int getVersion();
method public boolean hasExpired();
- method public boolean isHttpOnly();
method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
method public void setComment(java.lang.String);
method public void setCommentURL(java.lang.String);
method public void setDiscard(boolean);
method public void setDomain(java.lang.String);
- method public void setHttpOnly(boolean);
method public void setMaxAge(long);
method public void setPath(java.lang.String);
method public void setPortlist(java.lang.String);
@@ -42347,11 +42263,6 @@
method public abstract java.net.SocketImpl createSocketImpl();
}
- public abstract interface SocketOption {
- method public abstract java.lang.String name();
- method public abstract java.lang.Class<T> type();
- }
-
public abstract interface SocketOptions {
method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -42382,21 +42293,6 @@
ctor public SocketTimeoutException(java.lang.String);
}
- public final class StandardSocketOptions {
- ctor public StandardSocketOptions();
- field public static final java.net.SocketOption IP_MULTICAST_IF;
- field public static final java.net.SocketOption IP_MULTICAST_LOOP;
- field public static final java.net.SocketOption IP_MULTICAST_TTL;
- field public static final java.net.SocketOption IP_TOS;
- field public static final java.net.SocketOption SO_BROADCAST;
- field public static final java.net.SocketOption SO_KEEPALIVE;
- field public static final java.net.SocketOption SO_LINGER;
- field public static final java.net.SocketOption SO_RCVBUF;
- field public static final java.net.SocketOption SO_REUSEADDR;
- field public static final java.net.SocketOption SO_SNDBUF;
- field public static final java.net.SocketOption TCP_NODELAY;
- }
-
public final class URI implements java.lang.Comparable java.io.Serializable {
ctor public URI(java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
@@ -42492,7 +42388,6 @@
method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public java.lang.String getContentEncoding();
method public int getContentLength();
- method public long getContentLengthLong();
method public java.lang.String getContentType();
method public long getDate();
method public static boolean getDefaultAllowUserInteraction();
@@ -42507,7 +42402,6 @@
method public long getHeaderFieldDate(java.lang.String, long);
method public int getHeaderFieldInt(java.lang.String, int);
method public java.lang.String getHeaderFieldKey(int);
- method public long getHeaderFieldLong(java.lang.String, long);
method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
method public long getIfModifiedSince();
method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -42858,10 +42752,6 @@
package java.nio.channels {
- public class AlreadyBoundException extends java.lang.IllegalStateException {
- ctor public AlreadyBoundException();
- }
-
public class AlreadyConnectedException extends java.lang.IllegalStateException {
ctor public AlreadyConnectedException();
}
@@ -42909,32 +42799,25 @@
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
- method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
- method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
- method public java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.DatagramSocket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
- public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
+ public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected FileChannel();
method public abstract void force(boolean) throws java.io.IOException;
method public final java.nio.channels.FileLock lock() throws java.io.IOException;
@@ -42966,7 +42849,6 @@
public abstract class FileLock implements java.lang.AutoCloseable {
ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
- method public java.nio.channels.Channel acquiredBy();
method public final java.nio.channels.FileChannel channel();
method public final void close() throws java.io.IOException;
method public final boolean isShared();
@@ -42999,32 +42881,6 @@
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
- public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
- method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
- method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
- }
-
public class NoConnectionPendingException extends java.lang.IllegalStateException {
ctor public NoConnectionPendingException();
}
@@ -43075,15 +42931,6 @@
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
}
- public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
- method public abstract long position() throws java.io.IOException;
- method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
- method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract long size() throws java.io.IOException;
- method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
- method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
- }
-
public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
ctor protected SelectableChannel();
method public abstract java.lang.Object blockingLock();
@@ -43132,27 +42979,18 @@
method public abstract java.nio.channels.Selector wakeup();
}
- public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
+ public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
- method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
- method public java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.ServerSocket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
}
- public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
- method public java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean finishConnect() throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
method public abstract boolean isConnectionPending();
method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
@@ -43160,9 +42998,7 @@
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
- method public java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.Socket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -47122,7 +46958,6 @@
method public java.lang.String getDisplayName(java.util.Locale);
method public static java.util.Currency getInstance(java.lang.String);
method public static java.util.Currency getInstance(java.util.Locale);
- method public int getNumericCode();
method public java.lang.String getSymbol();
method public java.lang.String getSymbol(java.util.Locale);
}
@@ -47407,13 +47242,6 @@
method public int getWidth();
}
- public class IllformedLocaleException extends java.lang.RuntimeException {
- ctor public IllformedLocaleException();
- ctor public IllformedLocaleException(java.lang.String);
- ctor public IllformedLocaleException(java.lang.String, int);
- method public int getErrorIndex();
- }
-
public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable {
ctor public InputMismatchException();
ctor public InputMismatchException(java.lang.String);
@@ -47528,7 +47356,6 @@
ctor public Locale(java.lang.String, java.lang.String);
ctor public Locale(java.lang.String, java.lang.String, java.lang.String);
method public java.lang.Object clone();
- method public static java.util.Locale forLanguageTag(java.lang.String);
method public static java.util.Locale[] getAvailableLocales();
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
@@ -47538,24 +47365,15 @@
method public java.lang.String getDisplayLanguage(java.util.Locale);
method public final java.lang.String getDisplayName();
method public java.lang.String getDisplayName(java.util.Locale);
- method public java.lang.String getDisplayScript();
- method public java.lang.String getDisplayScript(java.util.Locale);
method public final java.lang.String getDisplayVariant();
method public java.lang.String getDisplayVariant(java.util.Locale);
- method public java.lang.String getExtension(char);
- method public java.util.Set<java.lang.Character> getExtensionKeys();
method public java.lang.String getISO3Country();
method public java.lang.String getISO3Language();
method public static java.lang.String[] getISOCountries();
method public static java.lang.String[] getISOLanguages();
method public java.lang.String getLanguage();
- method public java.lang.String getScript();
- method public java.util.Set<java.lang.String> getUnicodeLocaleAttributes();
- method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
- method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
method public static synchronized void setDefault(java.util.Locale);
- method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
field public static final java.util.Locale CANADA_FRENCH;
@@ -47573,33 +47391,14 @@
field public static final java.util.Locale KOREA;
field public static final java.util.Locale KOREAN;
field public static final java.util.Locale PRC;
- field public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x'
field public static final java.util.Locale ROOT;
field public static final java.util.Locale SIMPLIFIED_CHINESE;
field public static final java.util.Locale TAIWAN;
field public static final java.util.Locale TRADITIONAL_CHINESE;
field public static final java.util.Locale UK;
- field public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u'
field public static final java.util.Locale US;
}
- public static final class Locale.Builder {
- ctor public Locale.Builder();
- method public java.util.Locale.Builder addUnicodeLocaleAttribute(java.lang.String);
- method public java.util.Locale build();
- method public java.util.Locale.Builder clear();
- method public java.util.Locale.Builder clearExtensions();
- method public java.util.Locale.Builder removeUnicodeLocaleAttribute(java.lang.String);
- method public java.util.Locale.Builder setExtension(char, java.lang.String);
- method public java.util.Locale.Builder setLanguage(java.lang.String);
- method public java.util.Locale.Builder setLanguageTag(java.lang.String);
- method public java.util.Locale.Builder setLocale(java.util.Locale);
- method public java.util.Locale.Builder setRegion(java.lang.String);
- method public java.util.Locale.Builder setScript(java.lang.String);
- method public java.util.Locale.Builder setUnicodeLocaleKeyword(java.lang.String, java.lang.String);
- method public java.util.Locale.Builder setVariant(java.lang.String);
- }
-
public abstract interface Map {
method public abstract void clear();
method public abstract boolean containsKey(java.lang.Object);
@@ -48286,35 +48085,6 @@
method public V replace(K, V);
}
- public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
- ctor public ConcurrentLinkedDeque();
- ctor public ConcurrentLinkedDeque(java.util.Collection<? extends E>);
- method public void addFirst(E);
- method public void addLast(E);
- method public java.util.Iterator<E> descendingIterator();
- method public E element();
- method public E getFirst();
- method public E getLast();
- method public java.util.Iterator<E> iterator();
- method public boolean offer(E);
- method public boolean offerFirst(E);
- method public boolean offerLast(E);
- method public E peek();
- method public E peekFirst();
- method public E peekLast();
- method public E poll();
- method public E pollFirst();
- method public E pollLast();
- method public E pop();
- method public void push(E);
- method public E remove();
- method public E removeFirst();
- method public boolean removeFirstOccurrence(java.lang.Object);
- method public E removeLast();
- method public boolean removeLastOccurrence(java.lang.Object);
- method public int size();
- }
-
public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
ctor public ConcurrentLinkedQueue();
ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>);
@@ -48555,94 +48325,6 @@
method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService);
}
- public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService {
- ctor public ForkJoinPool();
- ctor public ForkJoinPool(int);
- ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
- method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
- method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
- method public void execute(java.util.concurrent.ForkJoinTask<?>);
- method public void execute(java.lang.Runnable);
- method public int getActiveThreadCount();
- method public boolean getAsyncMode();
- method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
- method public int getParallelism();
- method public int getPoolSize();
- method public int getQueuedSubmissionCount();
- method public long getQueuedTaskCount();
- method public int getRunningThreadCount();
- method public long getStealCount();
- method public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler();
- method public boolean hasQueuedSubmissions();
- method public T invoke(java.util.concurrent.ForkJoinTask<T>);
- method public boolean isQuiescent();
- method public boolean isShutdown();
- method public boolean isTerminated();
- method public boolean isTerminating();
- method public static void managedBlock(java.util.concurrent.ForkJoinPool.ManagedBlocker) throws java.lang.InterruptedException;
- method protected java.util.concurrent.ForkJoinTask<?> pollSubmission();
- method public void shutdown();
- method public java.util.List<java.lang.Runnable> shutdownNow();
- method public java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.ForkJoinTask<T>);
- field public static final java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory;
- }
-
- public static abstract interface ForkJoinPool.ForkJoinWorkerThreadFactory {
- method public abstract java.util.concurrent.ForkJoinWorkerThread newThread(java.util.concurrent.ForkJoinPool);
- }
-
- public static abstract interface ForkJoinPool.ManagedBlocker {
- method public abstract boolean block() throws java.lang.InterruptedException;
- method public abstract boolean isReleasable();
- }
-
- public abstract class ForkJoinTask implements java.util.concurrent.Future java.io.Serializable {
- ctor public ForkJoinTask();
- method public static java.util.concurrent.ForkJoinTask<?> adapt(java.lang.Runnable);
- method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
- method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
- method public boolean cancel(boolean);
- method public void complete(V);
- method public void completeExceptionally(java.lang.Throwable);
- method protected abstract boolean exec();
- method public final java.util.concurrent.ForkJoinTask<V> fork();
- method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
- method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
- method public final java.lang.Throwable getException();
- method public static java.util.concurrent.ForkJoinPool getPool();
- method public static int getQueuedTaskCount();
- method public abstract V getRawResult();
- method public static int getSurplusQueuedTaskCount();
- method public static void helpQuiesce();
- method public static boolean inForkJoinPool();
- method public final V invoke();
- method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>, java.util.concurrent.ForkJoinTask<?>);
- method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>...);
- method public static java.util.Collection<T> invokeAll(java.util.Collection<T>);
- method public final boolean isCancelled();
- method public final boolean isCompletedAbnormally();
- method public final boolean isCompletedNormally();
- method public final boolean isDone();
- method public final V join();
- method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
- method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
- method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
- method public final void quietlyInvoke();
- method public final void quietlyJoin();
- method public void reinitialize();
- method protected abstract void setRawResult(V);
- method public boolean tryUnfork();
- }
-
- public class ForkJoinWorkerThread extends java.lang.Thread {
- ctor protected ForkJoinWorkerThread(java.util.concurrent.ForkJoinPool);
- method public java.util.concurrent.ForkJoinPool getPool();
- method public int getPoolIndex();
- method protected void onStart();
- method protected void onTermination(java.lang.Throwable);
- }
-
public abstract interface Future {
method public abstract boolean cancel(boolean);
method public abstract V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
@@ -48727,52 +48409,6 @@
method public E take() throws java.lang.InterruptedException;
}
- public class LinkedTransferQueue extends java.util.AbstractQueue implements java.io.Serializable java.util.concurrent.TransferQueue {
- ctor public LinkedTransferQueue();
- ctor public LinkedTransferQueue(java.util.Collection<? extends E>);
- method public int drainTo(java.util.Collection<? super E>);
- method public int drainTo(java.util.Collection<? super E>, int);
- method public int getWaitingConsumerCount();
- method public boolean hasWaitingConsumer();
- method public java.util.Iterator<E> iterator();
- method public boolean offer(E, long, java.util.concurrent.TimeUnit);
- method public boolean offer(E);
- method public E peek();
- method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public E poll();
- method public void put(E);
- method public int remainingCapacity();
- method public int size();
- method public E take() throws java.lang.InterruptedException;
- method public void transfer(E) throws java.lang.InterruptedException;
- method public boolean tryTransfer(E);
- method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- }
-
- public class Phaser {
- ctor public Phaser();
- ctor public Phaser(int);
- ctor public Phaser(java.util.concurrent.Phaser);
- ctor public Phaser(java.util.concurrent.Phaser, int);
- method public int arrive();
- method public int arriveAndAwaitAdvance();
- method public int arriveAndDeregister();
- method public int awaitAdvance(int);
- method public int awaitAdvanceInterruptibly(int) throws java.lang.InterruptedException;
- method public int awaitAdvanceInterruptibly(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
- method public int bulkRegister(int);
- method public void forceTermination();
- method public int getArrivedParties();
- method public java.util.concurrent.Phaser getParent();
- method public final int getPhase();
- method public int getRegisteredParties();
- method public java.util.concurrent.Phaser getRoot();
- method public int getUnarrivedParties();
- method public boolean isTerminated();
- method protected boolean onAdvance(int, int);
- method public int register();
- }
-
public class PriorityBlockingQueue extends java.util.AbstractQueue implements java.util.concurrent.BlockingQueue java.io.Serializable {
ctor public PriorityBlockingQueue();
ctor public PriorityBlockingQueue(int);
@@ -48793,22 +48429,6 @@
method public E take() throws java.lang.InterruptedException;
}
- public abstract class RecursiveAction extends java.util.concurrent.ForkJoinTask {
- ctor public RecursiveAction();
- method protected abstract void compute();
- method protected final boolean exec();
- method public final java.lang.Void getRawResult();
- method protected final void setRawResult(java.lang.Void);
- }
-
- public abstract class RecursiveTask extends java.util.concurrent.ForkJoinTask {
- ctor public RecursiveTask();
- method protected abstract V compute();
- method protected final boolean exec();
- method public final V getRawResult();
- method protected final void setRawResult(V);
- }
-
public class RejectedExecutionException extends java.lang.RuntimeException {
ctor public RejectedExecutionException();
ctor public RejectedExecutionException(java.lang.String);
@@ -48847,14 +48467,12 @@
method protected java.util.concurrent.RunnableScheduledFuture<V> decorateTask(java.util.concurrent.Callable<V>, java.util.concurrent.RunnableScheduledFuture<V>);
method public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy();
method public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy();
- method public boolean getRemoveOnCancelPolicy();
method public java.util.concurrent.ScheduledFuture<?> schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
method public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean);
method public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean);
- method public void setRemoveOnCancelPolicy(boolean);
}
public class Semaphore implements java.io.Serializable {
@@ -48900,15 +48518,6 @@
method public abstract java.lang.Thread newThread(java.lang.Runnable);
}
- public class ThreadLocalRandom extends java.util.Random {
- method public static java.util.concurrent.ThreadLocalRandom current();
- method public double nextDouble(double);
- method public double nextDouble(double, double);
- method public int nextInt(int, int);
- method public long nextLong(long);
- method public long nextLong(long, long);
- }
-
public class ThreadPoolExecutor extends java.util.concurrent.AbstractExecutorService {
ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>);
ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory);
@@ -48996,14 +48605,6 @@
ctor public TimeoutException(java.lang.String);
}
- public abstract interface TransferQueue implements java.util.concurrent.BlockingQueue {
- method public abstract int getWaitingConsumerCount();
- method public abstract boolean hasWaitingConsumer();
- method public abstract void transfer(E) throws java.lang.InterruptedException;
- method public abstract boolean tryTransfer(E);
- method public abstract boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- }
-
}
package java.util.concurrent.atomic {
@@ -49213,7 +48814,6 @@
method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final boolean hasContended();
- method public final boolean hasQueuedPredecessors();
method public final boolean hasQueuedThreads();
method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method protected boolean isHeldExclusively();
@@ -49260,7 +48860,6 @@
method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method public final boolean hasContended();
- method public final boolean hasQueuedPredecessors();
method public final boolean hasQueuedThreads();
method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method protected boolean isHeldExclusively();
@@ -50205,10 +49804,8 @@
public class ZipFile implements java.io.Closeable {
ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
- ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException;
ctor public ZipFile(java.lang.String) throws java.io.IOException;
ctor public ZipFile(java.io.File, int) throws java.io.IOException;
- ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
method public java.lang.String getComment();
@@ -50262,7 +49859,6 @@
public class ZipInputStream extends java.util.zip.InflaterInputStream {
ctor public ZipInputStream(java.io.InputStream);
- ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -50310,7 +49906,6 @@
public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
ctor public ZipOutputStream(java.io.OutputStream);
- ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
method public void setComment(java.lang.String);
@@ -51809,7 +51404,6 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
- method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
method public abstract boolean getNeedClientAuth();
method public java.lang.String getPeerHost();
@@ -51883,12 +51477,10 @@
ctor public SSLParameters(java.lang.String[]);
ctor public SSLParameters(java.lang.String[], java.lang.String[]);
method public java.lang.String[] getCipherSuites();
- method public java.lang.String getEndpointIdentificationAlgorithm();
method public boolean getNeedClientAuth();
method public java.lang.String[] getProtocols();
method public boolean getWantClientAuth();
method public void setCipherSuites(java.lang.String[]);
- method public void setEndpointIdentificationAlgorithm(java.lang.String);
method public void setNeedClientAuth(boolean);
method public void setProtocols(java.lang.String[]);
method public void setWantClientAuth(boolean);
@@ -51989,7 +51581,6 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
- method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract boolean getNeedClientAuth();
method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract javax.net.ssl.SSLSession getSession();
@@ -52045,14 +51636,6 @@
method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine);
}
- public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager {
- ctor public X509ExtendedTrustManager();
- method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
- method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
- method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
- method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
- }
-
public abstract interface X509KeyManager implements javax.net.ssl.KeyManager {
method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket);
method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8945526..9c1f1d1 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -32,6 +32,7 @@
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -45,6 +46,8 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.view.IWindowManager;
+import android.view.View;
+
import com.android.internal.os.BaseCommand;
import dalvik.system.VMRuntime;
@@ -123,6 +126,7 @@
" am stack info <STACK_ID>\n" +
" am lock-task <TASK_ID>\n" +
" am lock-task stop\n" +
+ " am get-config\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -229,6 +233,9 @@
"\n" +
"am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" +
"\n" +
+ "am get-config: retrieve the configuration and any recent configurations\n" +
+ " of the device\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@@ -323,6 +330,8 @@
runStack();
} else if (op.equals("lock-task")) {
runLockTask();
+ } else if (op.equals("get-config")) {
+ runGetConfig();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -909,8 +918,7 @@
}
}
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
- abi)) {
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
@@ -1699,4 +1707,23 @@
} catch (RemoteException e) {
}
}
+
+ private void runGetConfig() throws Exception {
+ try {
+ Configuration config = mAm.getConfiguration();
+ if (config == null) {
+ System.err.println("Activity manager has no configuration");
+ return;
+ }
+
+ System.out.println("config: " + Configuration.resourceQualifierString(config));
+ System.out.print("abi: " + Build.CPU_ABI);
+ if (!Build.CPU_ABI2.isEmpty()) {
+ System.out.print("," + Build.CPU_ABI2);
+ }
+ System.out.println();
+
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5867232..a480219 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -189,7 +189,9 @@
/** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
public static final int OP_GET_USAGE_STATS = 43;
/** @hide */
- public static final int _NUM_OP = 44;
+ public static final int OP_MUTE_MICROPHONE = 44;
+ /** @hide */
+ public static final int _NUM_OP = 45;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -257,6 +259,7 @@
OP_COARSE_LOCATION,
OP_COARSE_LOCATION,
OP_GET_USAGE_STATS,
+ OP_MUTE_MICROPHONE
};
/**
@@ -308,6 +311,7 @@
OPSTR_MONITOR_LOCATION,
OPSTR_MONITOR_HIGH_POWER_LOCATION,
null,
+ null,
};
/**
@@ -358,7 +362,8 @@
"WAKE_LOCK",
"MONITOR_LOCATION",
"MONITOR_HIGH_POWER_LOCATION",
- "GET_USAGE_STATS"
+ "GET_USAGE_STATS",
+ "OP_MUTE_MICROPHONE",
};
/**
@@ -410,6 +415,7 @@
null, // no permission for generic location monitoring
null, // no permission for high power location monitoring
android.Manifest.permission.PACKAGE_USAGE_STATS,
+ null, // no permission for muting/unmuting microphone
};
/**
@@ -462,6 +468,7 @@
null, //MONITOR_LOCATION
null, //MONITOR_HIGH_POWER_LOCATION
null, //GET_USAGE_STATS
+ UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
};
/**
@@ -512,6 +519,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -566,6 +574,7 @@
false,
false,
false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 52d4585..ab3bb49 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -118,6 +118,7 @@
import android.service.fingerprint.FingerprintManager;
import android.service.fingerprint.FingerprintManagerReceiver;
import android.service.fingerprint.FingerprintService;
+import android.telecomm.TelecommManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -142,6 +143,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
+import com.android.internal.telecomm.ITelecommService;
import java.io.File;
import java.io.FileInputStream;
@@ -554,6 +556,13 @@
return new TelephonyManager(ctx.getOuterContext());
}});
+ registerService(TELECOMM_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(TELECOMM_SERVICE);
+ return new TelecommManager(ctx.getOuterContext(),
+ ITelecommService.Stub.asInterface(b));
+ }});
+
registerService(UI_MODE_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new UiModeManager();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ccf8451..fd19b40 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2035,6 +2035,7 @@
AUDIO_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
+ TELECOMM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
TEXT_SERVICES_MANAGER_SERVICE,
@@ -2163,6 +2164,8 @@
* @see android.media.MediaRouter
* @see #TELEPHONY_SERVICE
* @see android.telephony.TelephonyManager
+ * @see #TELECOMM_SERVICE
+ * @see android.telecomm.TelecommManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -2494,6 +2497,16 @@
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.telecomm.TelecommManager} to manage telecomm-related features
+ * of the device.
+ *
+ * @see #getSystemService
+ * @see android.telecomm.TelecommManager
+ */
+ public static final String TELECOMM_SERVICE = "telecomm";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.text.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 550c1f1..8d9b8d9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -2873,16 +2874,17 @@
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final File sourceFile = new File(archiveFilePath);
- PackageParser.Package pkg = packageParser.parsePackage(
- sourceFile, archiveFilePath, metrics, 0);
- if (pkg == null) {
+ try {
+ PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics,
+ 0);
+ if ((flags & GET_SIGNATURES) != 0) {
+ packageParser.collectCertificates(pkg, 0);
+ }
+ PackageUserState state = new PackageUserState();
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
+ } catch (PackageParserException e) {
return null;
}
- if ((flags & GET_SIGNATURES) != 0) {
- packageParser.collectCertificates(pkg, 0);
- }
- PackageUserState state = new PackageUserState();
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4cac7fd..91895ff 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.ComponentName;
import android.content.Intent;
@@ -31,6 +32,7 @@
import android.os.Bundle;
import android.os.PatternMatcher;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
@@ -81,6 +83,8 @@
private static final boolean DEBUG_PARSER = false;
private static final boolean DEBUG_BACKUP = false;
+ // TODO: switch outError users to PackageParserException
+
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
@@ -207,10 +211,10 @@
}
}
- /* Light weight package info.
- * @hide
+ /**
+ * Lightweight parsed details about a single APK file.
*/
- public static class PackageLite {
+ public static class ApkLite {
public final String packageName;
public final String splitName;
public final int versionCode;
@@ -218,7 +222,7 @@
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
- public PackageLite(String packageName, String splitName, int versionCode,
+ public ApkLite(String packageName, String splitName, int versionCode,
int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) {
this.packageName = packageName;
this.splitName = splitName;
@@ -247,6 +251,10 @@
mArchiveSourcePath = archiveSourcePath;
}
+ public PackageParser(File archiveSource) {
+ this(archiveSource.getAbsolutePath());
+ }
+
public void setSeparateProcesses(String[] procs) {
mSeparateProcesses = procs;
}
@@ -255,6 +263,10 @@
mOnlyCoreApps = onlyCoreApps;
}
+ private static final boolean isPackageFilename(File file) {
+ return isPackageFilename(file.getName());
+ }
+
private static final boolean isPackageFilename(String name) {
return name.endsWith(".apk");
}
@@ -497,26 +509,84 @@
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_GET_SIGNATURES = 1<<8;
- public int getParseError() {
- return mParseError;
+ /**
+ * Parse all APK files under the given directory as a single package.
+ */
+ public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ final File[] files = apkDir.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "No packages found in split");
+ }
+
+ File baseFile = null;
+ for (File file : files) {
+ if (file.isFile() && isPackageFilename(file)) {
+ ApkLite lite = parseApkLite(file.getAbsolutePath(), 0);
+ if (lite == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + file);
+ }
+
+ if (TextUtils.isEmpty(lite.splitName)) {
+ baseFile = file;
+ break;
+ }
+ }
+ }
+
+ if (baseFile == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Missing base APK in " + apkDir);
+ }
+
+ final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay);
+ if (pkg == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Failed to parse base APK: " + baseFile);
+ }
+
+ for (File file : files) {
+ if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) {
+ parseSplitApk(pkg, file, metrics, flags, trustedOverlay);
+ }
+ }
+
+ // Always use a well-defined sort order
+ if (pkg.splitCodePaths != null) {
+ Arrays.sort(pkg.splitCodePaths);
+ }
+
+ return pkg;
}
- public Package parsePackage(File sourceFile, String destCodePath,
- DisplayMetrics metrics, int flags) {
- return parsePackage(sourceFile, destCodePath, metrics, flags, false);
+ public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags)
+ throws PackageParserException {
+ return parseMonolithicPackage(apkFile, metrics, flags, false);
}
- public Package parsePackage(File sourceFile, String destCodePath,
- DisplayMetrics metrics, int flags, boolean trustedOverlay) {
+ public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay);
+ if (pkg != null) {
+ return pkg;
+ } else {
+ throw new PackageParserException(mParseError, "Failed to parse " + apkFile);
+ }
+ }
+
+ private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
- mArchiveSourcePath = sourceFile.getPath();
- if (!sourceFile.isFile()) {
+ mArchiveSourcePath = apkFile.getAbsolutePath();
+ if (!apkFile.isFile()) {
Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
return null;
}
- if (!isPackageFilename(sourceFile.getName())
+ if (!isPackageFilename(apkFile.getName())
&& (flags&PARSE_MUST_BE_APK) != 0) {
if ((flags&PARSE_IS_SYSTEM) == 0) {
// We expect to have non-.apk files in the system dir,
@@ -560,13 +630,12 @@
Exception errorException = null;
try {
// XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);
+ pkg = parseBaseApk(res, parser, flags, trustedOverlay, errorText);
} catch (Exception e) {
errorException = e;
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
}
-
if (pkg == null) {
// If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
// just means to skip this app so don't make a fuss about it.
@@ -590,22 +659,25 @@
parser.close();
assmgr.close();
- // Set code and resource paths
- pkg.mPath = destCodePath;
- pkg.mScanPath = mArchiveSourcePath;
- //pkg.applicationInfo.sourceDir = destCodePath;
- //pkg.applicationInfo.publicSourceDir = destRes;
+ pkg.codePath = mArchiveSourcePath;
pkg.mSignatures = null;
return pkg;
}
+ private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ // TODO: expand split APK parsing
+ pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths,
+ apkFile.getAbsolutePath());
+ }
+
/**
* Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the
* APK. If it successfully scanned the package and found the
* {@code AndroidManifest.xml}, {@code true} is returned.
*/
- public boolean collectManifestDigest(Package pkg) {
+ public void collectManifestDigest(Package pkg) throws PackageParserException {
try {
final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
try {
@@ -616,13 +688,19 @@
} finally {
jarFile.close();
}
- return true;
} catch (IOException e) {
- return false;
+ throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Failed to collect manifest digest");
}
}
- public boolean collectCertificates(Package pkg, int flags) {
+ public void collectCertificates(Package pkg, int flags) throws PackageParserException {
+ if (!collectCertificatesInternal(pkg, flags)) {
+ throw new PackageParserException(mParseError, "Failed to collect certificates");
+ }
+ }
+
+ private boolean collectCertificatesInternal(Package pkg, int flags) {
pkg.mSignatures = null;
WeakReference<byte[]> readBufferRef;
@@ -808,7 +886,7 @@
* @param flags Special parse flags
* @return PackageLite object with package information or null on failure.
*/
- public static PackageLite parsePackageLite(String packageFilePath, int flags) {
+ public static ApkLite parseApkLite(String packageFilePath, int flags) {
AssetManager assmgr = null;
final XmlResourceParser parser;
final Resources res;
@@ -844,9 +922,9 @@
final AttributeSet attrs = parser;
final String errors[] = new String[1];
- PackageLite packageLite = null;
+ ApkLite packageLite = null;
try {
- packageLite = parsePackageLite(res, parser, attrs, flags, signatures, errors);
+ packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors);
} catch (PackageParserException e) {
Slog.w(TAG, packageFilePath, e);
} catch (IOException e) {
@@ -930,7 +1008,7 @@
(splitName != null) ? splitName.intern() : splitName);
}
- private static PackageLite parsePackageLite(Resources res, XmlPullParser parser,
+ private static ApkLite parseApkLite(Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, Signature[] signatures, String[] outError)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
@@ -972,7 +1050,7 @@
}
}
- return new PackageLite(packageSplit.first, packageSplit.second, versionCode,
+ return new ApkLite(packageSplit.first, packageSplit.second, versionCode,
installLocation, verifiers, signatures);
}
@@ -988,9 +1066,8 @@
return new Signature(sig);
}
- private Package parsePackage(
- Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,
- String[] outError) throws XmlPullParserException, IOException {
+ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
+ boolean trustedOverlay, String[] outError) throws XmlPullParserException, IOException {
AttributeSet attrs = parser;
mParseInstrumentationArgs = null;
@@ -1019,7 +1096,13 @@
}
}
- final Package pkg = new Package(pkgName, splitName);
+ if (!TextUtils.isEmpty(splitName)) {
+ outError[0] = "Expected base APK, but found split " + splitName;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+ return null;
+ }
+
+ final Package pkg = new Package(pkgName);
boolean foundApp = false;
TypedArray sa = res.obtainAttributes(attrs,
@@ -3580,10 +3663,17 @@
return true;
}
+ /**
+ * Representation of a full package parsed from APK files on disk. A package
+ * consists of a single base APK, and zero or more split APKs.
+ */
public final static class Package {
public String packageName;
- public String splitName;
+
+ // TODO: work towards making these paths invariant
+ public String codePath;
+ public String[] splitCodePaths;
// For now we only support one application per package.
public final ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -3615,9 +3705,6 @@
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
- // If this is a 3rd party app, this is the path of the zip file.
- public String mPath;
-
// The version code declared for this package.
public int mVersionCode;
@@ -3637,10 +3724,6 @@
// preferred up order.
public int mPreferredOrder = 0;
- // For use by the package manager to keep track of the path to the
- // file an app came from.
- public String mScanPath;
-
// For use by package manager to keep track of where it needs to do dexopt.
public boolean mDexOptNeeded = true;
@@ -3700,9 +3783,8 @@
public Set<PublicKey> mSigningKeys;
public Map<String, Set<PublicKey>> mKeySetMapping;
- public Package(String packageName, String splitName) {
+ public Package(String packageName) {
this.packageName = packageName;
- this.splitName = splitName;
applicationInfo.packageName = packageName;
applicationInfo.uid = -1;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index a07fc97..a83bd4a 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -17,11 +17,14 @@
package android.content.res;
import android.content.pm.ActivityInfo;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.view.View;
+import java.text.Format;
+import java.util.ArrayList;
import java.util.Locale;
/**
@@ -1306,4 +1309,240 @@
private static int getScreenLayoutNoDirection(int screenLayout) {
return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
}
+
+ /**
+ *
+ * @hide
+ */
+ public static String localeToResourceQualifier(Locale locale) {
+ StringBuilder sb = new StringBuilder();
+ boolean l = (locale.getLanguage().length() != 0);
+ boolean c = (locale.getCountry().length() != 0);
+ boolean s = (locale.getScript().length() != 0);
+ boolean v = (locale.getVariant().length() != 0);
+
+ if (l) {
+ sb.append(locale.getLanguage());
+ if (c) {
+ sb.append("-r").append(locale.getCountry());
+ if (s) {
+ sb.append("-s").append(locale.getScript());
+ if (v) {
+ sb.append("-v").append(locale.getVariant());
+ }
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Returns a string representation of the configuration that can be parsed
+ * by build tools (like AAPT).
+ *
+ *
+ *
+ * @hide
+ */
+ public static String resourceQualifierString(Configuration config) {
+ ArrayList<String> parts = new ArrayList<String>();
+
+ if (config.mcc != 0) {
+ parts.add(config.mcc + "mcc");
+ if (config.mnc != 0) {
+ parts.add(config.mnc + "mnc");
+ }
+ }
+
+ if (!config.locale.getLanguage().isEmpty()) {
+ parts.add(localeToResourceQualifier(config.locale));
+ }
+
+ switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
+ case Configuration.SCREENLAYOUT_LAYOUTDIR_LTR:
+ parts.add("ldltr");
+ break;
+ case Configuration.SCREENLAYOUT_LAYOUTDIR_RTL:
+ parts.add("ldrtl");
+ break;
+ default:
+ break;
+ }
+
+ if (config.smallestScreenWidthDp != 0) {
+ parts.add("sw" + config.smallestScreenWidthDp + "dp");
+ }
+
+ if (config.screenWidthDp != 0) {
+ parts.add("w" + config.screenWidthDp + "dp");
+ }
+
+ if (config.screenHeightDp != 0) {
+ parts.add("h" + config.screenHeightDp + "dp");
+ }
+
+ switch (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) {
+ case Configuration.SCREENLAYOUT_SIZE_SMALL:
+ parts.add("small");
+ break;
+ case Configuration.SCREENLAYOUT_SIZE_NORMAL:
+ parts.add("normal");
+ break;
+ case Configuration.SCREENLAYOUT_SIZE_LARGE:
+ parts.add("large");
+ break;
+ case Configuration.SCREENLAYOUT_SIZE_XLARGE:
+ parts.add("xlarge");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) {
+ case Configuration.SCREENLAYOUT_LONG_YES:
+ parts.add("long");
+ break;
+ case Configuration.SCREENLAYOUT_LONG_NO:
+ parts.add("notlong");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.orientation) {
+ case Configuration.ORIENTATION_LANDSCAPE:
+ parts.add("land");
+ break;
+ case Configuration.ORIENTATION_PORTRAIT:
+ parts.add("port");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
+ case Configuration.UI_MODE_TYPE_APPLIANCE:
+ parts.add("appliance");
+ break;
+ case Configuration.UI_MODE_TYPE_DESK:
+ parts.add("desk");
+ break;
+ case Configuration.UI_MODE_TYPE_TELEVISION:
+ parts.add("television");
+ break;
+ case Configuration.UI_MODE_TYPE_CAR:
+ parts.add("car");
+ break;
+ case Configuration.UI_MODE_TYPE_WATCH:
+ parts.add("watch");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
+ case Configuration.UI_MODE_NIGHT_YES:
+ parts.add("night");
+ break;
+ case Configuration.UI_MODE_NIGHT_NO:
+ parts.add("notnight");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.densityDpi) {
+ case 0:
+ break;
+ case 120:
+ parts.add("ldpi");
+ break;
+ case 160:
+ parts.add("mdpi");
+ break;
+ case 213:
+ parts.add("tvdpi");
+ break;
+ case 240:
+ parts.add("hdpi");
+ break;
+ case 320:
+ parts.add("xhdpi");
+ break;
+ default:
+ parts.add(config.densityDpi + "dpi");
+ break;
+ }
+
+ switch (config.touchscreen) {
+ case Configuration.TOUCHSCREEN_NOTOUCH:
+ parts.add("notouch");
+ break;
+ case Configuration.TOUCHSCREEN_FINGER:
+ parts.add("finger");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.keyboardHidden) {
+ case Configuration.KEYBOARDHIDDEN_NO:
+ parts.add("keysexposed");
+ break;
+ case Configuration.KEYBOARDHIDDEN_YES:
+ parts.add("keyshidden");
+ break;
+ case Configuration.KEYBOARDHIDDEN_SOFT:
+ parts.add("keyssoft");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.keyboard) {
+ case Configuration.KEYBOARD_NOKEYS:
+ parts.add("nokeys");
+ break;
+ case Configuration.KEYBOARD_QWERTY:
+ parts.add("qwerty");
+ break;
+ case Configuration.KEYBOARD_12KEY:
+ parts.add("12key");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.navigationHidden) {
+ case Configuration.NAVIGATIONHIDDEN_NO:
+ parts.add("navexposed");
+ break;
+ case Configuration.NAVIGATIONHIDDEN_YES:
+ parts.add("navhidden");
+ break;
+ default:
+ break;
+ }
+
+ switch (config.navigation) {
+ case Configuration.NAVIGATION_NONAV:
+ parts.add("nonav");
+ break;
+ case Configuration.NAVIGATION_DPAD:
+ parts.add("dpad");
+ break;
+ case Configuration.NAVIGATION_TRACKBALL:
+ parts.add("trackball");
+ break;
+ case Configuration.NAVIGATION_WHEEL:
+ parts.add("wheel");
+ break;
+ default:
+ break;
+ }
+
+ parts.add("v" + Build.VERSION.SDK_INT);
+ return TextUtils.join("-", parts);
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index a0bf552..559a469 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -40,6 +40,6 @@
void addDeviceEventListener(IHdmiDeviceEventListener listener);
void deviceSelect(int logicalAddress, IHdmiControlCallback callback);
void portSelect(int portId, IHdmiControlCallback callback);
- void sendKeyEvent(int keyCode);
+ void sendKeyEvent(int keyCode, boolean isPressed);
List<HdmiPortInfo> getPortInfo();
}
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index e32890d..67f6409 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -40,11 +40,19 @@
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- final TypedArray a = context.obtainStyledAttributes(
+ TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes);
setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax));
a.recycle();
- setLayoutResource(com.android.internal.R.layout.preference_widget_seekbar);
+
+ a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
+ final int layoutResId = a.getResourceId(
+ com.android.internal.R.styleable.SeekBarPreference_layout,
+ com.android.internal.R.layout.preference_widget_seekbar);
+ a.recycle();
+
+ setLayoutResource(layoutResId);
}
public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -52,7 +60,7 @@
}
public SeekBarPreference(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
+ this(context, attrs, com.android.internal.R.attr.seekBarPreferenceStyle);
}
public SeekBarPreference(Context context) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a5e9fd..06c05ee 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4537,6 +4537,12 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the primary phone application
+ * @hide
+ */
+ public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
+
+ /**
* Name of a package that the current user has explicitly allowed to see all of that
* user's notifications.
*
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index a6cddae..8fa45e6 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -80,6 +80,11 @@
private ITrustAgentServiceCallback mCallback;
+ private Runnable mPendingGrantTrustTask;
+
+ // Lock used to access mPendingGrantTrustTask and mCallback.
+ private final Object mLock = new Object();
+
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
@@ -127,12 +132,24 @@
* @param initiatedByUser indicates that the user has explicitly initiated an action that proves
* the user is about to use the device.
*/
- public final void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser) {
- if (mCallback != null) {
- try {
- mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
- } catch (RemoteException e) {
- onError("calling enableTrust()");
+ public final void grantTrust(
+ final CharSequence message, final long durationMs, final boolean initiatedByUser) {
+ synchronized (mLock) {
+ if (mCallback != null) {
+ try {
+ mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
+ } catch (RemoteException e) {
+ onError("calling enableTrust()");
+ }
+ } else {
+ // Remember trust has been granted so we can effectively grant it once the service
+ // is bound.
+ mPendingGrantTrustTask = new Runnable() {
+ @Override
+ public void run() {
+ grantTrust(message, durationMs, initiatedByUser);
+ }
+ };
}
}
}
@@ -141,11 +158,16 @@
* Call to revoke trust on the device.
*/
public final void revokeTrust() {
- if (mCallback != null) {
- try {
- mCallback.revokeTrust();
- } catch (RemoteException e) {
- onError("calling revokeTrust()");
+ synchronized (mLock) {
+ if (mPendingGrantTrustTask != null) {
+ mPendingGrantTrustTask = null;
+ }
+ if (mCallback != null) {
+ try {
+ mCallback.revokeTrust();
+ } catch (RemoteException e) {
+ onError("calling revokeTrust()");
+ }
}
}
}
@@ -164,7 +186,13 @@
}
public void setCallback(ITrustAgentServiceCallback callback) {
- mCallback = callback;
+ synchronized (mLock) {
+ mCallback = callback;
+ if (mPendingGrantTrustTask != null) {
+ mPendingGrantTrustTask.run();
+ mPendingGrantTrustTask = null;
+ }
+ }
}
}
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index d426d124..8674c66 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -38,7 +38,7 @@
* {@hide}
*/
void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, Paint p);
+ float x, float y, boolean isRtl, Paint p);
/**
* Just like {@link Paint#measureText}.
@@ -55,12 +55,12 @@
* @hide
*/
float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex, Paint paint);
+ boolean isRtl, float[] advances, int advancesIndex, Paint paint);
/**
* Just like {@link Paint#getTextRunCursor}.
* @hide
*/
- int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
+ int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
int cursorOpt, Paint p);
}
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index f8e3c83..2415b11 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -159,18 +159,17 @@
mPos = p + len;
if (mEasy) {
- int flags = mDir == Layout.DIR_LEFT_TO_RIGHT
- ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
- return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p);
+ boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
+ return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
}
float totalAdvance = 0;
int level = mLevels[p];
for (int q = p, i = p + 1, e = p + len;; ++i) {
if (i == e || mLevels[i] != level) {
- int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
+ boolean isRtl = (level & 0x1) != 0;
totalAdvance +=
- paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q);
+ paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
if (i == e) {
break;
}
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 1d9aa05..6b984d6 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -503,7 +503,7 @@
// Span watchers need to be called after text watchers, which may update the layout
sendToSpanWatchers(start, end, newLen - origLen);
- return this;
+ return this;
}
private static boolean hasNonExclusiveExclusiveSpanAt(CharSequence text, int offset) {
@@ -745,7 +745,7 @@
}
}
- return 0;
+ return 0;
}
/**
@@ -1117,20 +1117,20 @@
* {@hide}
*/
public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, Paint p) {
+ float x, float y, boolean isRtl, Paint p) {
checkRange("drawTextRun", start, end);
int contextLen = contextEnd - contextStart;
int len = end - start;
if (contextEnd <= mGapStart) {
- c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, flags, p);
+ c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, isRtl, p);
} else if (contextStart >= mGapStart) {
c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength,
- contextLen, x, y, flags, p);
+ contextLen, x, y, isRtl, p);
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
- c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, flags, p);
+ c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, isRtl, p);
TextUtils.recycle(buf);
}
}
@@ -1187,7 +1187,7 @@
* Don't call this yourself -- exists for Paint to use internally.
* {@hide}
*/
- public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
+ public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, boolean isRtl,
float[] advances, int advancesPos, Paint p) {
float ret;
@@ -1197,15 +1197,15 @@
if (end <= mGapStart) {
ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
- flags, advances, advancesPos);
+ isRtl, advances, advancesPos);
} else if (start >= mGapStart) {
ret = p.getTextRunAdvances(mText, start + mGapLength, len,
- contextStart + mGapLength, contextLen, flags, advances, advancesPos);
+ contextStart + mGapLength, contextLen, isRtl, advances, advancesPos);
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, flags, advances, advancesPos);
+ 0, contextLen, isRtl, advances, advancesPos);
TextUtils.recycle(buf);
}
@@ -1228,7 +1228,7 @@
*
* @param contextStart the start index of the context
* @param contextEnd the (non-inclusive) end index of the context
- * @param flags either DIRECTION_RTL or DIRECTION_LTR
+ * @param dir either DIRECTION_RTL or DIRECTION_LTR
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
* CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1238,7 +1238,7 @@
* @deprecated This is an internal method, refrain from using it in your code
*/
@Deprecated
- public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
+ public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
int cursorOpt, Paint p) {
int ret;
@@ -1246,15 +1246,15 @@
int contextLen = contextEnd - contextStart;
if (contextEnd <= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart, contextLen,
- flags, offset, cursorOpt);
+ dir, offset, cursorOpt);
} else if (contextStart >= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
- flags, offset + mGapLength, cursorOpt) - mGapLength;
+ dir, offset + mGapLength, cursorOpt) - mGapLength;
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunCursor(buf, 0, contextLen,
- flags, offset - contextStart, cursorOpt) + contextStart;
+ dir, offset - contextStart, cursorOpt) + contextStart;
TextUtils.recycle(buf);
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index d892f19..c19cf32 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -664,14 +664,14 @@
}
}
- int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
+ int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
if (mCharsValid) {
return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
- flags, offset, cursorOpt);
+ dir, offset, cursorOpt);
} else {
return wp.getTextRunCursor(mText, mStart + spanStart,
- mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
+ mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
}
}
@@ -738,15 +738,14 @@
int contextLen = contextEnd - contextStart;
if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
- int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
if (mCharsValid) {
ret = wp.getTextRunAdvances(mChars, start, runLen,
- contextStart, contextLen, flags, null, 0);
+ contextStart, contextLen, runIsRtl, null, 0);
} else {
int delta = mStart;
ret = wp.getTextRunAdvances(mText, delta + start,
delta + end, delta + contextStart, delta + contextEnd,
- flags, null, 0);
+ runIsRtl, null, 0);
}
}
@@ -977,16 +976,15 @@
private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
- int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
if (mCharsValid) {
int count = end - start;
int contextCount = contextEnd - contextStart;
c.drawTextRun(mChars, start, count, contextStart, contextCount,
- x, y, flags, wp);
+ x, y, runIsRtl, wp);
} else {
int delta = mStart;
c.drawTextRun(mText, delta + start, delta + end,
- delta + contextStart, delta + contextEnd, x, y, flags, wp);
+ delta + contextStart, delta + contextEnd, x, y, runIsRtl, wp);
}
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 5056097..832d67a 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -51,10 +51,10 @@
private int mWidth;
private int mHeight;
-
+
private float[] mPoint;
private float[] mLine;
-
+
private Rect mClipBounds;
private RectF mPathBounds;
@@ -167,7 +167,7 @@
nSetViewport(mRenderer, width, height);
}
-
+
private static native void nSetViewport(long renderer, int width, int height);
@Override
@@ -208,22 +208,22 @@
/**
* Must match Caches::FlushMode values
- *
- * @see #flushCaches(int)
+ *
+ * @see #flushCaches(int)
*/
static final int FLUSH_CACHES_LAYERS = 0;
-
+
/**
* Must match Caches::FlushMode values
- *
- * @see #flushCaches(int)
+ *
+ * @see #flushCaches(int)
*/
static final int FLUSH_CACHES_MODERATE = 1;
/**
* Must match Caches::FlushMode values
- *
- * @see #flushCaches(int)
+ *
+ * @see #flushCaches(int)
*/
static final int FLUSH_CACHES_FULL = 2;
@@ -245,7 +245,7 @@
///////////////////////////////////////////////////////////////////////////
// Hardware layer
///////////////////////////////////////////////////////////////////////////
-
+
void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
layer.setLayerPaint(paint);
nDrawLayer(mRenderer, layer.getLayer(), x, y);
@@ -298,7 +298,7 @@
public boolean clipRect(float left, float top, float right, float bottom) {
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
}
-
+
private static native boolean nClipRect(long renderer, float left, float top,
float right, float bottom, int op);
@@ -311,14 +311,14 @@
public boolean clipRect(int left, int top, int right, int bottom) {
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
}
-
+
private static native boolean nClipRect(long renderer, int left, int top,
int right, int bottom, int op);
@Override
public boolean clipRect(Rect rect) {
return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
- Region.Op.INTERSECT.nativeInt);
+ Region.Op.INTERSECT.nativeInt);
}
@Override
@@ -360,7 +360,7 @@
public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
return nQuickReject(mRenderer, left, top, right, bottom);
}
-
+
private static native boolean nQuickReject(long renderer, float left, float top,
float right, float bottom);
@@ -385,7 +385,7 @@
public void translate(float dx, float dy) {
if (dx != 0.0f || dy != 0.0f) nTranslate(mRenderer, dx, dy);
}
-
+
private static native void nTranslate(long renderer, float dx, float dy);
@Override
@@ -399,21 +399,21 @@
public void rotate(float degrees) {
nRotate(mRenderer, degrees);
}
-
+
private static native void nRotate(long renderer, float degrees);
@Override
public void scale(float sx, float sy) {
nScale(mRenderer, sx, sy);
}
-
+
private static native void nScale(long renderer, float sx, float sy);
@Override
public void setMatrix(Matrix matrix) {
nSetMatrix(mRenderer, matrix == null ? 0 : matrix.native_instance);
}
-
+
private static native void nSetMatrix(long renderer, long matrix);
@SuppressWarnings("deprecation")
@@ -421,16 +421,16 @@
public void getMatrix(Matrix matrix) {
nGetMatrix(mRenderer, matrix.native_instance);
}
-
+
private static native void nGetMatrix(long renderer, long matrix);
@Override
public void concat(Matrix matrix) {
if (matrix != null) nConcatMatrix(mRenderer, matrix.native_instance);
}
-
+
private static native void nConcatMatrix(long renderer, long matrix);
-
+
///////////////////////////////////////////////////////////////////////////
// State management
///////////////////////////////////////////////////////////////////////////
@@ -439,14 +439,14 @@
public int save() {
return nSave(mRenderer, Canvas.CLIP_SAVE_FLAG | Canvas.MATRIX_SAVE_FLAG);
}
-
+
@Override
public int save(int saveFlags) {
return nSave(mRenderer, saveFlags);
}
private static native int nSave(long renderer, int flags);
-
+
@Override
public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
if (bounds != null) {
@@ -494,12 +494,12 @@
private static native int nSaveLayerAlpha(long renderer, float left, float top, float right,
float bottom, int alpha, int saveFlags);
-
+
@Override
public void restore() {
nRestore(mRenderer);
}
-
+
private static native void nRestore(long renderer);
@Override
@@ -508,12 +508,12 @@
}
private static native void nRestoreToCount(long renderer, int saveCount);
-
+
@Override
public int getSaveCount() {
return nGetSaveCount(mRenderer);
}
-
+
private static native int nGetSaveCount(long renderer);
///////////////////////////////////////////////////////////////////////////
@@ -739,7 +739,7 @@
public void drawColor(int color, PorterDuff.Mode mode) {
nDrawColor(mRenderer, color, mode.nativeInt);
}
-
+
private static native void nDrawColor(long renderer, int color, int mode);
@Override
@@ -930,7 +930,7 @@
nDrawText(mRenderer, text, index, count, x, y,
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
-
+
private static native void nDrawText(long renderer, char[] text, int index, int count,
float x, float y, int bidiFlags, long paint, long typeface);
@@ -997,49 +997,45 @@
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
+ float x, float y, boolean isRtl, Paint paint) {
if ((index | count | text.length - index - count) < 0) {
throw new IndexOutOfBoundsException();
}
- if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
- throw new IllegalArgumentException("Unknown direction: " + dir);
- }
- nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, isRtl,
paint.mNativePaint, paint.mNativeTypeface);
}
private static native void nDrawTextRun(long renderer, char[] text, int index, int count,
- int contextIndex, int contextCount, float x, float y, int dir, long nativePaint, long nativeTypeface);
+ int contextIndex, int contextCount, float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
- float x, float y, int dir, Paint paint) {
+ float x, float y, boolean isRtl, Paint paint) {
if ((start | end | end - start | text.length() - end) < 0) {
throw new IndexOutOfBoundsException();
}
- int flags = dir == 0 ? 0 : 1;
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
- contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ contextEnd, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, flags, paint);
+ contextStart, contextEnd, x, y, isRtl, paint);
} else {
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
- x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface);
TemporaryBuffer.recycle(buf);
}
}
private static native void nDrawTextRun(long renderer, String text, int start, int end,
- int contextStart, int contextEnd, float x, float y, int flags, long nativePaint, long nativeTypeface);
+ int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
@Override
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 3de8144..c0e42a3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -452,10 +453,10 @@
*
* @return A hardware renderer backed by OpenGL.
*/
- static HardwareRenderer create(boolean translucent) {
+ static HardwareRenderer create(Context context, boolean translucent) {
HardwareRenderer renderer = null;
if (GLES20Canvas.isAvailable()) {
- renderer = new ThreadedRenderer(translucent);
+ renderer = new ThreadedRenderer(context, translucent);
}
return renderer;
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2bfbd65..166edc2 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -16,21 +16,29 @@
package android.view;
+import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.TimeUtils;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
/**
* Hardware renderer that proxies the rendering to a render thread. Most calls
@@ -71,8 +79,8 @@
private Choreographer mChoreographer;
private boolean mProfilingEnabled;
- ThreadedRenderer(boolean translucent) {
- AtlasInitializer.sInstance.init();
+ ThreadedRenderer(Context context, boolean translucent) {
+ AtlasInitializer.sInstance.init(context);
long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr);
@@ -334,7 +342,7 @@
private AtlasInitializer() {}
- synchronized void init() {
+ synchronized void init(Context context) {
if (mInitialized) return;
IBinder binder = ServiceManager.getService("assetatlas");
if (binder == null) return;
@@ -346,6 +354,8 @@
if (buffer != null) {
long[] map = atlas.getMap();
if (map != null) {
+ // TODO Remove after fixing b/15425820
+ validateMap(context, map);
nSetAtlas(buffer, map);
mInitialized = true;
}
@@ -361,6 +371,30 @@
Log.w(LOG_TAG, "Could not acquire atlas", e);
}
}
+
+ private static void validateMap(Context context, long[] map) {
+ Log.d("Atlas", "Validating map...");
+ HashSet<Long> preloadedPointers = new HashSet<Long>();
+
+ // We only care about drawables that hold bitmaps
+ final Resources resources = context.getResources();
+ final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
+
+ final int count = drawables.size();
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = drawables.valueAt(i).getBitmap();
+ if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
+ preloadedPointers.add(bitmap.mNativeBitmap);
+ }
+ }
+
+ for (int i = 0; i < map.length; i += 4) {
+ if (!preloadedPointers.contains(map[i])) {
+ Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
+ map[i] = 0;
+ }
+ }
+ }
}
static native void setupShadersDiskCache(String cacheFile);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 41037a5..b9e56f3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6116,6 +6116,12 @@
*/
protected boolean fitSystemWindows(Rect insets) {
if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) {
+ if (insets == null) {
+ // Null insets by definition have already been consumed.
+ // This call cannot apply insets since there are none to apply,
+ // so return false.
+ return false;
+ }
// If we're not in the process of dispatching the newer apply insets call,
// that means we're not in the compatibility path. Dispatch into the newer
// apply insets path and take things from there.
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 3854f34..29e865f 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -23,7 +23,7 @@
* Defines common utilities for working with View's animations.
*
*/
-public class ViewAnimationUtils {
+public final class ViewAnimationUtils {
private ViewAnimationUtils() {}
/**
* Returns a ValueAnimator which can animate a clipping circle.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4cd1b25..dfd5cdf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -700,7 +700,7 @@
}
final boolean translucent = attrs.format != PixelFormat.OPAQUE;
- mAttachInfo.mHardwareRenderer = HardwareRenderer.create(translucent);
+ mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
mAttachInfo.mHardwareAccelerated =
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index ec396aa..470d413 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -408,7 +408,6 @@
* @return true if filePathCallback will be invoked, false to use default handling.
*
* @see FileChooserParams
- * @hide For API approval
*/
public boolean showFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
@@ -416,11 +415,8 @@
}
/**
- * Parameters used in the {@link #showFileChooser(WebView,ValueCallback<String[]>,FileChooserParams)}
- * method.
- *
+ * Parameters used in the {@link #showFileChooser} method.
* This is intended to be used as a read-only data struct by the application.
- * @hide For API approval
*/
public static class FileChooserParams {
// Flags for mode
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 22f5e23..0f51e8b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2727,7 +2727,6 @@
/**
* Sets whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
- * @hide
*/
@android.view.RemotableViewMethod
public final void setShowSoftInputOnFocus(boolean show) {
@@ -2738,7 +2737,6 @@
/**
* Returns whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
- * @hide
*/
public final boolean getShowSoftInputOnFocus() {
// When there is no Editor, return default true value
@@ -9062,11 +9060,11 @@
}
public void drawTextRun(Canvas c, int start, int end,
- int contextStart, int contextEnd, float x, float y, int flags, Paint p) {
+ int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p) {
int count = end - start;
int contextCount = contextEnd - contextStart;
c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
- contextCount, x, y, flags, p);
+ contextCount, x, y, isRtl, p);
}
public float measureText(int start, int end, Paint p) {
@@ -9078,20 +9076,20 @@
}
public float getTextRunAdvances(int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex,
+ int contextEnd, boolean isRtl, float[] advances, int advancesIndex,
Paint p) {
int count = end - start;
int contextCount = contextEnd - contextStart;
return p.getTextRunAdvances(mChars, start + mStart, count,
- contextStart + mStart, contextCount, flags, advances,
+ contextStart + mStart, contextCount, isRtl, advances,
advancesIndex);
}
- public int getTextRunCursor(int contextStart, int contextEnd, int flags,
+ public int getTextRunCursor(int contextStart, int contextEnd, int dir,
int offset, int cursorOpt, Paint p) {
int contextCount = contextEnd - contextStart;
return p.getTextRunCursor(mChars, contextStart + mStart,
- contextCount, flags, offset + mStart, cursorOpt);
+ contextCount, dir, offset + mStart, cursorOpt);
}
}
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 2b62552..8ee0a1b 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -22,6 +22,7 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
+import android.media.ClosedCaptionRenderer;
import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
@@ -316,6 +317,7 @@
context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
controller.registerRenderer(new WebVttRenderer(context));
controller.registerRenderer(new TtmlRenderer(context));
+ controller.registerRenderer(new ClosedCaptionRenderer(context));
mMediaPlayer.setSubtitleAnchor(controller, this);
if (mAudioSession != 0) {
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 6de3b9e..a5964c8 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -882,26 +882,26 @@
static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
jcharArray text, jint index, jint count,
- jfloat x, jfloat y, jint flags,
+ jfloat x, jfloat y, jint bidiFlags,
jlong paintHandle, jlong typefaceHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
- drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint, typeface);
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
jlong canvasHandle, jstring text,
jint start, jint end,
- jfloat x, jfloat y, jint flags,
+ jfloat x, jfloat y, jint bidiFlags,
jlong paintHandle, jlong typefaceHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
- drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint, typeface);
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
env->ReleaseStringChars(text, textArray);
}
@@ -951,24 +951,25 @@
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
int start, int end,
- jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) {
+ jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
jint count = end - start;
- drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, flags, paint, typeface);
+ drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
+ typeface);
}
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
int start, int count, int contextCount,
- jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) {
+ jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
#ifdef USE_MINIKIN
Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(textArray, start, count, contextCount, css);
drawGlyphsToSkia(canvas, paint, layout, x, y);
#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- textArray, start, count, contextCount, flags);
+ textArray, start, count, contextCount, bidiFlags);
if (value == NULL) {
return;
}
@@ -979,7 +980,8 @@
x -= value->getTotalAdvance();
}
paint->setTextAlign(SkPaint::kLeft_Align);
- doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint);
+ doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(),
+ x, y, paint);
doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint);
paint->setTextAlign(align);
#endif
@@ -990,42 +992,37 @@
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) {
- uint32_t flags;
- SkDrawFilter* drawFilter = canvas->getDrawFilter();
- if (drawFilter) {
- SkPaint paintCopy(*paint);
- drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
- flags = paintCopy.getFlags();
- } else {
- flags = paint->getFlags();
- }
- if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
- SkScalar left = x;
- SkScalar right = x + length;
- float textSize = paint->getTextSize();
- float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
- if (flags & SkPaint::kUnderlineText_Flag) {
- SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
- canvas->drawRectCoords(left, top, right, bottom, *paint);
+ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
+ SkPaint* paint) {
+ uint32_t flags;
+ SkDrawFilter* drawFilter = canvas->getDrawFilter();
+ if (drawFilter) {
+ SkPaint paintCopy(*paint);
+ drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ flags = paintCopy.getFlags();
+ } else {
+ flags = paint->getFlags();
}
- if (flags & SkPaint::kStrikeThruText_Flag) {
- SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
- canvas->drawRectCoords(left, top, right, bottom, *paint);
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ SkScalar left = x;
+ SkScalar right = x + length;
+ float textSize = paint->getTextSize();
+ float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+ canvas->drawRectCoords(left, top, right, bottom, *paint);
+ }
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+ canvas->drawRectCoords(left, top, right, bottom, *paint);
+ }
}
}
-}
-
- static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
- // Beware: this needs Glyph encoding (already done on the Paint constructor)
- canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
- }
static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
- int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
+ int index, int count, jfloat x, jfloat y, SkPaint* paint) {
SkPoint* posPtr = new SkPoint[count];
for (int indx = 0; indx < count; indx++) {
posPtr[indx].fX = x + posArray[indx * 2];
@@ -1035,33 +1032,35 @@
delete[] posPtr;
}
- static void drawTextRun___CIIIIFFIPaintTypeface(
+ static void drawTextRun___CIIIIFFZPaintTypeface(
JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
jint count, jint contextIndex, jint contextCount,
- jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+ jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
jchar* chars = env->GetCharArrayElements(text, NULL);
drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
- count, contextCount, x, y, dirFlags, paint, typeface);
+ count, contextCount, x, y, bidiFlags, paint, typeface);
env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
}
- static void drawTextRun__StringIIIIFFIPaintTypeface(
+ static void drawTextRun__StringIIIIFFZPaintTypeface(
JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
jint end, jint contextStart, jint contextEnd,
- jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+ jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
jint count = end - start;
jint contextCount = contextEnd - contextStart;
const jchar* chars = env->GetStringChars(text, NULL);
drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
- count, contextCount, x, y, dirFlags, paint, typeface);
+ count, contextCount, x, y, bidiFlags, paint, typeface);
env->ReleaseStringChars(text, chars);
}
@@ -1268,10 +1267,10 @@
(void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
{"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
(void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
- {"native_drawTextRun","(J[CIIIIFFIJJ)V",
- (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaintTypeface},
- {"native_drawTextRun","(JLjava/lang/String;IIIIFFIJJ)V",
- (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaintTypeface},
+ {"native_drawTextRun","(J[CIIIIFFZJJ)V",
+ (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
+ {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
+ (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
{"native_drawPosText","(J[CII[FJ)V",
(void*) SkCanvasGlue::drawPosText___CII_FPaint},
{"native_drawPosText","(JLjava/lang/String;[FJ)V",
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 8b11d31..1e40d94 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -707,7 +707,7 @@
}
static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, TypefaceImpl* typeface, const jchar *text,
- jint start, jint count, jint contextCount, jint flags,
+ jint start, jint count, jint contextCount, jboolean isRtl,
jfloatArray advances, jint advancesIndex) {
NPE_CHECK_RETURN_ZERO(env, paint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -729,14 +729,16 @@
jfloat* advancesArray = new jfloat[count];
jfloat totalAdvance = 0;
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+
#ifdef USE_MINIKIN
Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, start, count, contextCount, css);
layout.getAdvances(advancesArray);
totalAdvance = layout.getAdvance();
#else
- TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags,
+ TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, bidiFlags,
advancesArray, &totalAdvance);
#endif
@@ -747,28 +749,28 @@
return totalAdvance;
}
- static jfloat getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ static jfloat getTextRunAdvances___CIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle,
jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
- jint flags, jfloatArray advances, jint advancesIndex) {
+ jboolean isRtl, jfloatArray advances, jint advancesIndex) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex,
- index - contextIndex, count, contextCount, flags, advances, advancesIndex);
+ index - contextIndex, count, contextCount, isRtl, advances, advancesIndex);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
- static jfloat getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ static jfloat getTextRunAdvances__StringIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle,
- jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
+ jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl,
jfloatArray advances, jint advancesIndex) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart,
- start - contextStart, end - start, contextEnd - contextStart, flags,
+ start - contextStart, end - start, contextEnd - contextStart, isRtl,
advances, advancesIndex);
env->ReleaseStringChars(text, textArray);
return result;
@@ -819,21 +821,21 @@
}
static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
- jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) {
+ jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
- jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags,
+ jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
offset, cursorOpt);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
- jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) {
+ jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
jint result = doTextRunCursor(env, paint, textArray, contextStart,
- contextEnd - contextStart, flags, offset, cursorOpt);
+ contextEnd - contextStart, dir, offset, cursorOpt);
env->ReleaseStringChars(text, textArray);
return result;
}
@@ -1110,10 +1112,10 @@
{"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
{"native_getTextWidths","(JJ[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
{"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
- {"native_getTextRunAdvances","(JJ[CIIIII[FI)F",
- (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI},
- {"native_getTextRunAdvances","(JJLjava/lang/String;IIIII[FI)F",
- (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
+ {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
+ (void*) SkPaintGlue::getTextRunAdvances___CIIIIZ_FI},
+ {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F",
+ (void*) SkPaintGlue::getTextRunAdvances__StringIIIIZ_FI},
{"native_getTextGlyphs","(JLjava/lang/String;IIIII[C)I",
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9fa5ec9..4a6e117 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -641,16 +641,16 @@
#endif
static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
- jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) {
+ jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
#ifdef USE_MINIKIN
Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, 0, count, count, css);
x += xOffsetForTextAlign(paint, layout.getAdvance());
renderTextLayout(renderer, &layout, x, y, paint);
#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, flags);
+ text, 0, count, count, bidiFlags);
if (value == NULL) {
return;
}
@@ -670,9 +670,9 @@
}
static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
- SkPath* path, jfloat hOffset, jfloat vOffset, int flags, SkPaint* paint) {
+ SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, flags);
+ text, 0, count, count, bidiFlags);
if (value == NULL) {
return;
}
@@ -685,16 +685,16 @@
static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
jint start, jint count, jint contextCount, jfloat x, jfloat y,
- int flags, SkPaint* paint, TypefaceImpl* typeface) {
+ int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
#ifdef USE_MINIKIN
Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, start, count, contextCount, css);
x += xOffsetForTextAlign(paint, layout.getAdvance());
renderTextLayout(renderer, &layout, x, y, paint);
#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, start, count, contextCount, flags);
+ text, start, count, contextCount, bidiFlags);
if (value == NULL) {
return;
}
@@ -715,71 +715,72 @@
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
jlong rendererPtr, jcharArray text, jint index, jint count,
- jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) {
+ jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
- renderText(renderer, textArray + index, count, x, y, flags, paint, typeface);
+ renderText(renderer, textArray + index, count, x, y, bidiFlags, paint, typeface);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz,
jlong rendererPtr, jstring text, jint start, jint end,
- jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) {
+ jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
const jchar* textArray = env->GetStringChars(text, NULL);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
- renderText(renderer, textArray + start, end - start, x, y, flags, paint, typeface);
+ renderText(renderer, textArray + start, end - start, x, y, bidiFlags, paint, typeface);
env->ReleaseStringChars(text, textArray);
}
static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
jlong rendererPtr, jcharArray text, jint index, jint count,
- jlong pathPtr, jfloat hOffset, jfloat vOffset, jint flags, jlong paintPtr) {
+ jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
renderTextOnPath(renderer, textArray + index, count, path,
- hOffset, vOffset, flags, paint);
+ hOffset, vOffset, bidiFlags, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
jlong rendererPtr, jstring text, jint start, jint end,
- jlong pathPtr, jfloat hOffset, jfloat vOffset, jint flags, jlong paintPtr) {
+ jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
const jchar* textArray = env->GetStringChars(text, NULL);
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
renderTextOnPath(renderer, textArray + start, end - start, path,
- hOffset, vOffset, flags, paint);
+ hOffset, vOffset, bidiFlags, paint);
env->ReleaseStringChars(text, textArray);
}
static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz,
jlong rendererPtr, jcharArray text, jint index, jint count,
- jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags,
+ jint contextIndex, jint contextCount, jfloat x, jfloat y, jboolean isRtl,
jlong paintPtr, jlong typefacePtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
- count, contextCount, x, y, dirFlags, paint, typeface);
+ count, contextCount, x, y, bidiFlags, paint, typeface);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz,
jlong rendererPtr, jstring text, jint start, jint end,
- jint contextStart, int contextEnd, jfloat x, jfloat y, jint dirFlags,
+ jint contextStart, int contextEnd, jfloat x, jfloat y, jboolean isRtl,
jlong paintPtr, jlong typefacePtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
const jchar* textArray = env->GetStringChars(text, NULL);
@@ -788,15 +789,16 @@
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
renderTextRun(renderer, textArray + contextStart, start - contextStart,
- count, contextCount, x, y, dirFlags, paint, typeface);
+ count, contextCount, x, y, bidiFlags, paint, typeface);
env->ReleaseStringChars(text, textArray);
}
static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count,
- const jfloat* positions, jint dirFlags, SkPaint* paint) {
+ const jfloat* positions, jint bidiFlags, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, dirFlags);
+ text, 0, count, count, bidiFlags);
if (value == NULL) {
return;
}
@@ -988,8 +990,8 @@
{ "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJ)V",
(void*) android_view_GLES20Canvas_drawTextOnPath },
- { "nDrawTextRun", "(J[CIIIIFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
- { "nDrawTextRun", "(JLjava/lang/String;IIIIFFIJJ)V",
+ { "nDrawTextRun", "(J[CIIIIFFZJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
+ { "nDrawTextRun", "(JLjava/lang/String;IIIIFFZJJ)V",
(void*) android_view_GLES20Canvas_drawTextRun },
{ "nDrawPosText", "(J[CII[FJ)V", (void*) android_view_GLES20Canvas_drawPosTextArray },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c58bf04..0cdddba 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,46 +14,40 @@
* limitations under the License.
*/
-#include "android_runtime/AndroidRuntime.h"
+#define LOG_TAG "Zygote"
// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
#include <sys/mount.h>
#include <linux/fs.h>
#include <grp.h>
+#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <stdlib.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
#include <unistd.h>
-#include <fcntl.h>
+#include <sys/capability.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
-#include "cutils/fs.h"
-#include "cutils/multiuser.h"
-#include "cutils/sched_policy.h"
-#include "utils/String8.h"
+
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <cutils/sched_policy.h>
+#include <utils/String8.h>
+#include <selinux/android.h>
+
+#include "android_runtime/AndroidRuntime.h"
#include "JNIHelp.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#if defined(HAVE_PRCTL)
-#include <sys/prctl.h>
-#endif
-
-#include <selinux/android.h>
-
-#if defined(__linux__)
-#include <sys/personality.h>
-#include <sys/utsname.h>
-#if defined(HAVE_ANDROID_OS)
-#include <sys/capability.h>
-#endif
-#endif
-
namespace {
using android::String8;
@@ -97,11 +91,9 @@
if (WTERMSIG(status) != SIGKILL) {
ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
}
-#ifdef WCOREDUMP
if (WCOREDUMP(status)) {
ALOGI("Process %d dumped core.", pid);
}
-#endif /* ifdef WCOREDUMP */
}
// If the just-crashed process is the system_server, bring down zygote
@@ -199,8 +191,6 @@
}
}
-#if defined(HAVE_ANDROID_OS)
-
// The debug malloc library needs to know whether it's the zygote or a child.
extern "C" int gMallocLeakZygoteChild;
@@ -254,17 +244,6 @@
}
}
-#else
-
-static int gMallocLeakZygoteChild = 0;
-
-static void EnableKeepCapabilities(JNIEnv*) {}
-static void DropCapabilitiesBoundingSet(JNIEnv*) {}
-static void SetCapabilities(JNIEnv*, int64_t, int64_t) {}
-static void SetSchedulerPolicy(JNIEnv*) {}
-
-#endif
-
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode) {
@@ -337,7 +316,6 @@
return true;
}
-#if defined(__linux__)
static bool NeedsNoRandomizeWorkaround() {
#if !defined(__arm__)
return false;
@@ -357,7 +335,6 @@
return (major < 3) || ((major == 3) && (minor < 4));
#endif
}
-#endif
// Utility to close down the Zygote socket file descriptors while
// the child is still running as root with Zygote's privileges. Each
@@ -474,7 +451,6 @@
RuntimeAbort(env);
}
-#if defined(__linux__)
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
@@ -483,58 +459,49 @@
ALOGW("personality(%d) failed", new_personality);
}
}
-#endif
SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
SetSchedulerPolicy(env);
-#if defined(HAVE_ANDROID_OS)
- { // NOLINT(whitespace/braces)
- const char* se_info_c_str = NULL;
- ScopedUtfChars* se_info = NULL;
- if (java_se_info != NULL) {
- se_info = new ScopedUtfChars(env, java_se_info);
- se_info_c_str = se_info->c_str();
- if (se_info_c_str == NULL) {
- ALOGE("se_info_c_str == NULL");
- RuntimeAbort(env);
- }
- }
- const char* se_name_c_str = NULL;
- ScopedUtfChars* se_name = NULL;
- if (java_se_name != NULL) {
- se_name = new ScopedUtfChars(env, java_se_name);
- se_name_c_str = se_name->c_str();
- if (se_name_c_str == NULL) {
- ALOGE("se_name_c_str == NULL");
- RuntimeAbort(env);
- }
- }
- rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
- if (rc == -1) {
- ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
- is_system_server, se_info_c_str, se_name_c_str);
- RuntimeAbort(env);
- }
-
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (se_info_c_str == NULL && is_system_server) {
- se_name_c_str = "system_server";
- }
- if (se_info_c_str != NULL) {
- SetThreadName(se_name_c_str);
- }
-
- delete se_info;
- delete se_name;
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ ALOGE("se_info_c_str == NULL");
+ RuntimeAbort(env);
+ }
}
-#else
- UNUSED(is_system_server);
- UNUSED(java_se_info);
- UNUSED(java_se_name);
-#endif
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ ALOGE("se_name_c_str == NULL");
+ RuntimeAbort(env);
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str);
+ RuntimeAbort(env);
+ }
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_info_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_info_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
UnsetSigChldHandler();
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 17acfc5..c5c0e97 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 17acfc5..c5c0e97 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
index 9b8ca22..3b31225 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
index 9b8ca22..3b31225 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
index bc20f6c..b65009e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
index bc20f6c..b65009e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
index 571819b..a2dfcae 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
index 571819b..c3fda0e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 1f83b5a..bae60a7 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 1f83b5a..bae60a7 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
index 733cf45..a9653b0 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
index 733cf45..a9653b0 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
index 2265de4..394cb5e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
index 2265de4..394cb5e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
index f3ada58..aa23c6e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
index f3ada58..028b3b8 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 3fdd3bc..4e6d076 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 3fdd3bc..4e6d076 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
index eaa02b3..ca61cb2 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
index eaa02b3..ca61cb2 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
index 28c8b94..b5999be 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
index 28c8b94..b5999be 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
index 6090cce..8392ac3 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
index 6090cce..522bafd 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 3f2e982..ebb2f8b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 3f2e982..ebb2f8b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
index 14b958b..3fa20ca 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
index 14b958b..3fa20ca 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
index 4db22d4..6cc59ed 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
index 4db22d4..6cc59ed 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
index a11e1c7..a1fcd08 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
index a11e1c7..c6c0224 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index c08deab..1e45530 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index c08deab..1e45530 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
index 8b1a55c..2c63c5d 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
index 8b1a55c..2c63c5d 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
index 77cd1fa..dd5e26e 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
index 77cd1fa..dd5e26e 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
index e0e3540..aa9b3c5 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
index e0e3540..367c25a 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 324e490..df28ad0 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 324e490..df28ad0 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
index e126cc6..3a27831 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
index e126cc6..3a27831 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
index 4c1f1b9..d68bdf42 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
index 4c1f1b9..d68bdf42 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
index 219d37b..da03ec9 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
index 219d37b..482b249 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/layout/preference_widget_seekbar.xml b/core/res/res/layout/preference_widget_seekbar.xml
index c427965..05daa1a 100644
--- a/core/res/res/layout/preference_widget_seekbar.xml
+++ b/core/res/res/layout/preference_widget_seekbar.xml
@@ -14,9 +14,7 @@
limitations under the License.
-->
-<!-- Layout for a Preference in a PreferenceActivity. The
- Preference is able to place a specific widget for its particular
- type in the "widget_frame" layout. -->
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/preference_widget_seekbar_material.xml b/core/res/res/layout/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..f70a472
--- /dev/null
+++ b/core/res/res/layout/preference_widget_seekbar_material.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:minWidth="@dimen/preference_icon_minWidth"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:minWidth="48dp"
+ />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="8dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1">
+
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="4" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/title"
+ android:minWidth="@dimen/preference_widget_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+ <SeekBar android:id="@+android:id/seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_toEndOf="@android:id/widget_frame"
+ android:layout_alignParentEnd="true" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5213896..e807d69 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -834,6 +834,8 @@
<attr name="preferenceFragmentPaddingSide" format="dimension" />
<!-- Default style for switch preferences. -->
<attr name="switchPreferenceStyle" format="reference" />
+ <!-- Default style for seekbar preferences. -->
+ <attr name="seekBarPreferenceStyle" format="reference" />
<!-- ============================ -->
<!-- Text selection handle styles -->
@@ -6607,6 +6609,10 @@
<attr name="disableDependentsState" />
</declare-styleable>
+ <declare-styleable name="SeekBarPreference">
+ <attr name="layout" />
+ </declare-styleable>
+
<!-- Use <code>tts-engine</code> as the root tag of the XML resource that
describes a text to speech engine implemented as a subclass of
{@link android.speech.tts.TextToSpeechService}.
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5bd6122..d6be3133 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -980,6 +980,10 @@
<item name="android:switchTextOff">@android:string/capital_off</item>
</style>
+ <style name="Preference.SeekBarPreference">
+ <item name="android:layout">@android:layout/preference_widget_seekbar</item>
+ </style>
+
<style name="Preference.PreferenceScreen">
</style>
@@ -1035,6 +1039,10 @@
<item name="android:switchTextOff">@android:string/capital_off</item>
</style>
+ <style name="Preference.Holo.SeekBarPreference">
+ <item name="android:layout">@android:layout/preference_widget_seekbar</item>
+ </style>
+
<style name="Preference.Holo.PreferenceScreen">
</style>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 84dbc79..84d38ce 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -254,6 +254,7 @@
<style name="Preference.DeviceDefault.PreferenceScreen" parent="Preference.Material.PreferenceScreen"/>
<style name="Preference.DeviceDefault.RingtonePreference" parent="Preference.Material.RingtonePreference"/>
<style name="Preference.DeviceDefault.SwitchPreference" parent="Preference.Material.SwitchPreference"/>
+ <style name="Preference.DeviceDefault.SeekBarPreference" parent="Preference.Material.SeekBarPreference"/>
<!-- AlertDialog Styles -->
<style name="AlertDialog.DeviceDefault" parent="AlertDialog.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 0c95149..25307b9 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -64,6 +64,10 @@
<item name="switchTextOff">@string/capital_off</item>
</style>
+ <style name="Preference.Material.SeekBarPreference">
+ <item name="layout">@android:layout/preference_widget_seekbar_material</item>
+ </style>
+
<style name="Preference.Material.PreferenceScreen"/>
<style name="Preference.Material.DialogPreference">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d69f60a..5f4553b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1893,5 +1893,6 @@
<java-symbol type="color" name="timepicker_default_selector_color_material" />
<java-symbol type="color" name="timepicker_default_numbers_background_color_material" />
<java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" />
+ <java-symbol type="attr" name="seekBarPreferenceStyle" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index cb5cb0c..5eec197 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -321,6 +321,7 @@
<item name="preferenceInformationStyle">@android:style/Preference.Information</item>
<item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@android:style/Preference.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@android:style/Preference.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
<item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
@@ -1203,6 +1204,7 @@
<item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item>
<item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@android:style/Preference.Holo.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item>
<item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item>
@@ -1543,6 +1545,7 @@
<item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item>
<item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@android:style/Preference.Holo.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item>
<item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 27c8754..8e83e48 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -160,6 +160,7 @@
<item name="preferenceInformationStyle">@style/Preference.DeviceDefault.Information</item>
<item name="checkBoxPreferenceStyle">@style/Preference.DeviceDefault.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@style/Preference.DeviceDefault.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@style/Preference.DeviceDefault.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference</item>
<item name="editTextPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.EditTextPreference</item>
@@ -421,6 +422,7 @@
<item name="preferenceInformationStyle">@style/Preference.DeviceDefault.Information</item>
<item name="checkBoxPreferenceStyle">@style/Preference.DeviceDefault.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@style/Preference.DeviceDefault.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@style/Preference.DeviceDefault.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference</item>
<item name="editTextPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.EditTextPreference</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 769c8a1..77e4307 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -282,6 +282,7 @@
<item name="preferenceInformationStyle">@style/Preference.Material.Information</item>
<item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@style/Preference.Material.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item>
<item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item>
@@ -634,6 +635,7 @@
<item name="preferenceInformationStyle">@style/Preference.Material.Information</item>
<item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item>
<item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item>
+ <item name="seekBarPreferenceStyle">@style/Preference.Material.SeekBarPreference</item>
<item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item>
<item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 07a6a10..7251e7c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.net.Uri;
@@ -60,7 +61,6 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -303,14 +303,13 @@
return Uri.fromFile(outFile);
}
- private PackageParser.Package parsePackage(Uri packageURI) {
+ private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException {
final String archiveFilePath = packageURI.getPath();
PackageParser packageParser = new PackageParser(archiveFilePath);
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath,
- metrics, 0);
+ PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0);
packageParser = null;
return pkg;
}
@@ -579,18 +578,18 @@
PackageParser.Package pkg;
- InstallParams(String outFileName, int rawResId) {
+ InstallParams(String outFileName, int rawResId) throws PackageParserException {
this.pkg = getParsedPackage(outFileName, rawResId);
- this.packageURI = Uri.fromFile(new File(pkg.mScanPath));
+ this.packageURI = Uri.fromFile(new File(pkg.codePath));
}
InstallParams(PackageParser.Package pkg) {
- this.packageURI = Uri.fromFile(new File(pkg.mScanPath));
+ this.packageURI = Uri.fromFile(new File(pkg.codePath));
this.pkg = pkg;
}
long getApkSize() {
- File file = new File(pkg.mScanPath);
+ File file = new File(pkg.codePath);
return file.length();
}
}
@@ -691,7 +690,8 @@
}
}
- private PackageParser.Package getParsedPackage(String outFileName, int rawResId) {
+ private PackageParser.Package getParsedPackage(String outFileName, int rawResId)
+ throws PackageParserException {
PackageManager pm = mContext.getPackageManager();
File filesDir = mContext.getFilesDir();
File outFile = new File(filesDir, outFileName);
@@ -1343,7 +1343,7 @@
assertUninstalled(info);
}
} finally {
- File outFile = new File(ip.pkg.mScanPath);
+ File outFile = new File(ip.pkg.codePath);
if (outFile != null && outFile.exists()) {
outFile.delete();
}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
index 58b7db9..42de9ea 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
@@ -36,8 +36,7 @@
}
}
- private void setLocale(String code) {
- Locale locale = new Locale(code);
+ private void setLocale(Locale locale) {
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
@@ -268,193 +267,193 @@
*/
public void testMatrix100000() throws Throwable {
final int resId = R.integer.matrix_100000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 100);
}
public void testMatrix100001() throws Throwable {
final int resId = R.integer.matrix_100001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100010() throws Throwable {
final int resId = R.integer.matrix_100010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 500);
}
public void testMatrix100011() throws Throwable {
final int resId = R.integer.matrix_100011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100100() throws Throwable {
final int resId = R.integer.matrix_100100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100101() throws Throwable {
final int resId = R.integer.matrix_100101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix100110() throws Throwable {
final int resId = R.integer.matrix_100110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100111() throws Throwable {
final int resId = R.integer.matrix_100111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101000() throws Throwable {
final int resId = R.integer.matrix_101000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 300);
}
public void testMatrix101001() throws Throwable {
final int resId = R.integer.matrix_101001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101010() throws Throwable {
final int resId = R.integer.matrix_101010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 500);
}
public void testMatrix101011() throws Throwable {
final int resId = R.integer.matrix_101011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101100() throws Throwable {
final int resId = R.integer.matrix_101100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101101() throws Throwable {
final int resId = R.integer.matrix_101101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101110() throws Throwable {
final int resId = R.integer.matrix_101110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101111() throws Throwable {
final int resId = R.integer.matrix_101111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix110000() throws Throwable {
final int resId = R.integer.matrix_110000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110001() throws Throwable {
final int resId = R.integer.matrix_110001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110010() throws Throwable {
final int resId = R.integer.matrix_110010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110011() throws Throwable {
final int resId = R.integer.matrix_110011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110100() throws Throwable {
final int resId = R.integer.matrix_110100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110101() throws Throwable {
final int resId = R.integer.matrix_110101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix110110() throws Throwable {
final int resId = R.integer.matrix_110110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110111() throws Throwable {
final int resId = R.integer.matrix_110111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111000() throws Throwable {
final int resId = R.integer.matrix_111000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111001() throws Throwable {
final int resId = R.integer.matrix_111001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111010() throws Throwable {
final int resId = R.integer.matrix_111010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111011() throws Throwable {
final int resId = R.integer.matrix_111011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111100() throws Throwable {
final int resId = R.integer.matrix_111100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111101() throws Throwable {
final int resId = R.integer.matrix_111101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111110() throws Throwable {
final int resId = R.integer.matrix_111110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111111() throws Throwable {
final int resId = R.integer.matrix_111111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index abb960c..6437e07 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -42,12 +42,21 @@
$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf))
$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf))
+extra_font_files := \
+ DroidSans.ttf \
+ DroidSans-Bold.ttf
+
################################
# On space-constrained devices, we include a subset of fonts:
ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+
droidsans_fallback_src := DroidSansFallback.ttf
-extra_font_files := DroidSans.ttf DroidSans-Bold.ttf
-else
+
+else # !SMALLER_FONT_FOOTPRINT
+
+droidsans_fallback_src := DroidSansFallbackFull.ttf
+
+ifneq ($(EXTENDED_FONT_FOOTPRINT),true)
include $(CLEAR_VARS)
LOCAL_MODULE := MTLmr3m.ttf
LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -55,12 +64,9 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
+extra_font_files += MTLmr3m.ttf
+endif # !EXTENDED_FONT_FOOTPRINT
-droidsans_fallback_src := DroidSansFallbackFull.ttf
-extra_font_files := \
- DroidSans.ttf \
- DroidSans-Bold.ttf \
- MTLmr3m.ttf
endif # SMALLER_FONT_FOOTPRINT
################################
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index c2d5afe..1eaae65 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -219,6 +219,26 @@
</family>
<family>
<fileset>
+ <file lang="zh-CN">NotoSansHans-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="zh-TW">NotoSansHant-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="ja">NotoSansJP-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="ko">NotoSansKR-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NanumGothic.ttf</file>
</fileset>
</family>
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index 402a95f..5331d1e 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -1,4 +1,4 @@
-page.title=Accessing Google Play Services APIs
+page.title=Accessing Google APIs
page.tags="oauth 2.0","GoogleAuthUtil"
trainingnavtop=true
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 6ff00c0..b4028bd 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -3,11 +3,11 @@
######## ATTENTION ###############
######## ###############
#########################################################
-
+
IF YOU MAKE CHANGES TO THIS FILE, YOU MUST GENERATE THE
GMS REFERENCE DOCS, BECAUSE THEY ARE NOT INCLUDED IN THE
DOCS BUILD RULE.
-
+
#########################################################
#########################################################
?>
@@ -73,11 +73,11 @@
</div>
<ul>
<li><a href="<?cs var:toroot?>google/play-services/setup.html">
- <span class="en">Setup</span></a>
+ <span class="en">Setting Up Google Play Services</span></a>
</li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>google/auth/api-client.html">
- <span class="en">Accessing Google Play Services APIs</span></a>
+ <span class="en">Accessing Google APIs</span></a>
</div>
<ul>
<li>
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index 744e191..049e6fe 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -1,4 +1,4 @@
-page.title=Set Up Google Play Services SDK
+page.title=Setting Up Google Play Services
@jd:body
@@ -9,7 +9,7 @@
<h2>In this document</h2>
<ol>
<li><a href="#Install">Install the Google Play Services SDK</a></li>
- <li><a href="#Setup">Set Up a Project that Uses Google Play Services</a></li>
+ <li><a href="#Setup">Add Google Play Services to Your Project</a></li>
<li><a href="#Proguard">Create a Proguard Exception</a></li>
<li><a href="#ensure">Ensure Devices Have the Google Play services APK</a></li>
</ol>
@@ -18,12 +18,17 @@
</div>
</div>
+<script>
+$(document).ready(function() {
+ setupIdeDocToggle();
+});
+</script>
-
+
<p>To develop an app using the <a href="{@docRoot}reference/gms-packages.html">Google
-Play services APIs</a>, you must download the Google Play services SDK
-from the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.
-The download includes the client library and code samples.</p>
+Play services APIs</a>, you need to set up your project with the Google Play services SDK.
+<p>If you haven't installed the Google Play services SDK yet, go get it now by following the guide
+to <a href="{@docRoot}sdk/installing/adding-packages.html">Adding SDK Packages</a>.</p>
<p>To test your app when using the Google Play services SDK, you must use either:</p>
<ul>
@@ -33,68 +38,25 @@
that runs the Google APIs platform based on Android 4.2.2 or higher.</li>
</ul>
-<p>Ideally, you should develop and test your app on a variety of devices, including
-both phones and tablets.</p>
-<h2 id="Install">Install the Google Play Services SDK</h2>
+<h2 id="Setup">Add Google Play Services to Your Project</h2>
-<p>To install the Google Play services SDK for development:</p>
+<p>
+<select class="ide">
+ <option value="eclipse">Using Eclipse with ADT</option>
+ <option value="studio">Using Android Studio</option>
+ <option value="other">Using something else</option>
+</select>
+</p>
+
+
+<div class="select-ide studio">
<ol>
- <li>Launch the SDK Manager in one of the following ways:
- <ul>
- <li>In Android Studio, click <strong>SDK Manager</strong>
-<img src="{@docRoot}images/tools/sdk-manager-studio.png" style="vertical-align:bottom;margin:0;height:19px" />
-in the toolbar.</li>
- <li>In Eclipse (with <a href="{@docRoot}tools/help/adt.html">ADT</a>),
- select <strong>Window</strong> > <strong>Android SDK Manager</strong>.</li>
- <li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android
- SDK directory.</li>
- <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
- Android SDK, then execute <code>android sdk</code>.</li>
- </ul>
- </li>
- <li>Install the Google Play services SDK.
- <p>Scroll to the bottom of the package list, expand <b>Extras</b>, select
- <b>Google Play services</b>, and install it. If you're using Android Studio, also install
- <b>Google Repository</b> (it provides the Maven repository used for Gradle builds).</p>
- <p>The Google Play services SDK is saved in your Android SDK environment at
- <code><android-sdk>/extras/google/google_play_services/</code>.</p>
-
-<p class="note"><strong>Note:</strong> Google Play services 4.0.30 (released
-November 2013) and newer versions require Android 2.3 or higher. If your app supports Android 2.2,
-you can continue development with the Google Play services SDK, but must instead install
-<b>Google Play services for Froyo</b> from the SDK Manager.</p>
-
- </li>
- <li>Install a compatible version of the Google APIs platform.
- <p>If you want to test your app on the emulator, expand the directory for <b>Android 4.2.2
- (API 17)</b> or a higher version, select <b>Google APIs</b>, and install it. Then create a
- new <a href="{@docRoot}tools/devices/index.html">AVD</a> with Google APIs as
- the platform target.</p>
- </li>
- <li>Make a copy of the Google Play services library project.
- <p class="note"><strong>Note:</strong> If you are using Android Studio, skip this step.</p>
- <p>Copy the library project at
- <code><android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/</code>
- to the location where you maintain your Android app projects.
- <p>If you are using Eclipse, import the library project into your workspace.
- Click <b>File > Import</b>, select <b>Android > Existing
- Android Code into Workspace</b>, and browse to the copy of the library project to import it.</p>
- </li>
-</ol>
-
-
-
-<h2 id="Setup">Set Up a Project that Uses Google Play Services</h2>
-
-<p><b>Using Android Studio:</b></p>
-
-<ol>
- <li>Open the <code>build.gradle</code> file inside your application module directory.
- <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
- <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
+ <li>Open the <code>build.gradle</code> file inside your application module directory.
+ <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
+ <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
Be sure to edit the file for your application module. See
<a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with
Gradle</a> for more information about Gradle.</p></li>
@@ -128,8 +90,11 @@
<p>You can now begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end studio -->
-<p><b>Using Eclipse or another IDE:</b></p>
+
+
+<div class="select-ide eclipse other">
<p>To make the Google Play services APIs available to your app, you must reference the library
project you created in step 4 of the <a href="#Install">installation instructions</a>.</p>
@@ -156,6 +121,9 @@
you can begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end eclipse and other -->
+
+
<h2 id="Proguard">Create a Proguard Exception</h2>
@@ -236,5 +204,6 @@
that takes the user to Google Play Store to install the update.</p>
-<p>To then begin a connection to Google Play services, read <a
-href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services APIs</a>.</p>
+<p>To then begin a connection to Google Play services (required by most Google APIs such
+as Google Drive, Google+, and Games), read <a
+href="{@docRoot}google/auth/api-client.html">Accessing Google APIs</a>.</p>
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 606baf8..de2980b 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -1,6 +1,5 @@
fullpage=true
page.viewport_width=970
-no_footer_links=true
excludeFromSuggestions=true
page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3f61-WpRguHq-aNjtF7xJjMTSi79as" />
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index 84a2d85..e44e9f3 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -3,7 +3,8 @@
fullpage=true
no_footer_links=true
page.type=about
-
+page.metaDescription=Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches.
+page.image={@docRoot}preview/images/hero.jpg
@jd:body
<style>
@@ -22,19 +23,6 @@
}
</style>
-<div id="video-container">
- <div id="video-frame">
- <div class="video-close">
- <span id="icon-video-close"> </span>
- </div>
- <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
- <div id="ytapiplayer">
- <a href="http://www.youtube.com/watch?v=0xQ3y902DEQ"><img width=940
- src="https://i1.ytimg.com/vi/0xQ3y902DEQ/maxresdefault.jpg"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
- </div>
- </div>
-</div>
-
<div class="landing-body-content">
<div class="landing-hero-container">
<div class="landing-section preview-hero">
@@ -93,10 +81,10 @@
<p>A New UI Design</p>
<p class="landing-small">
Create a consistent experience across mobile and the web with
- <b>Material</b>, the new Google design standard.
+ material design, the new Google-wide standard.
</p>
<p class="landing-small">
- <a href="/preview/quantum/index.html">Learn about</a>
+ <a href="/preview/material/index.html">Learn about material</a>
</p>
</div>
<div class="col-4">
@@ -108,7 +96,7 @@
</p>
<p class="landing-small">
- <a href="/preview/api-overview.html#art">Learn more</a>
+ <a href="/preview/api-overview.html#ART">Learn about ART</a>
</p>
</div>
<div class="col-4">
@@ -119,7 +107,7 @@
how they look, and automatic syncing to non-handheld devices.
</p>
<p class="landing-small">
- <a href="/preview/api-overview#graphics">Learn more</a>
+ <a href="/preview/api-overview.html#UI">Learn more</a>
</p>
</div>
<div class="col-4">
@@ -130,7 +118,7 @@
to give you more control over resource usage.
</p>
<p class="landing-small">
- <a href="/preview/api-overview#multimedia.html">Learn more</a>
+ <a href="/preview/api-overview.html#Power">Learn more</a>
</p>
</div>
</div>
@@ -174,13 +162,13 @@
<img class="landing-social-image" src="{@docRoot}preview/images/bugs.png" alt="">
</a>
<div class="landing-social-copy">
- <p>Submit Bugs</p>
+ <p>Issue Tracker</p>
<p class="landing-small">
Let us know when you encounter problems, so we can fix them and make
the platform better for you and your users.
</p><p class="landing-small">
<a target="_blank" href="http://submit-bugs!">
- Submit Bugs</a>
+ Report Issues</a>
</p>
<p></p>
</div>
@@ -190,13 +178,13 @@
<img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="">
</a>
<div class="landing-social-copy">
- <p>Discuss on Google+ </p>
+ <p>Google+ </p>
<p class="landing-small">
Join the community of Android developers testing out the L Developer Preview and
share your thoughts and experiences.
</p><p class="landing-small">
<a target="_blank" href="http://plus.google.com">
- Socialize on Google+</a>
+ Discuss on Google+</a>
</p>
</div>
</div>
@@ -205,14 +193,14 @@
<img class="landing-social-image" src="{@docRoot}preview/images/updates.png" alt="">
</a>
<div class="landing-social-copy">
- <p>Get Updates</p>
+ <p>Support and Updates</p>
<p class="landing-small">
Updates to the L Developer Preview are delivered
- in the Android SDK Manager. Check back here
+ in the Android SDK Manager. Check back periodically
for news about the changes.
</p>
<p class="landing-small">
- <a target="_blank" href="{@docRoot}preview/release-notes.html">See Release Notes</a>
+ <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a>
</p>
</div>
</div>
@@ -236,8 +224,6 @@
License</a>.
</div>
</div>
-
-
</div> <!-- end landing-body-content -->
<script>
diff --git a/docs/html/preview/material/animations.jd b/docs/html/preview/material/animations.jd
index 8297c65..cee782a 100644
--- a/docs/html/preview/material/animations.jd
+++ b/docs/html/preview/material/animations.jd
@@ -2,9 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#touch">Touch Feedback</a></li>
+ <li><a href="#reveal">Reveal Effect</a></li>
+ <li><a href="#transitions">Activity Transitions</a></li>
+ <li><a href="#curvedmotion">Curved Motion</a></li>
+ <li><a href="#viewstate">View State Changes</a></li>
+ <li><a href="#drawabletint">Drawable Tinting</a></li>
+</ol>
+</div>
+</div>
<p>Animations in material design give users feedback on their actions and provide visual
-continuity as users interact with your app. The Material theme provides some default animations
+continuity as users interact with your app. The material theme provides some default animations
for buttons and activity transitions, and the Android L Developer Preview provides additional
APIs that let you customize these animations and create new ones:</p>
@@ -17,7 +30,7 @@
</ul>
-<h2 style="margin-top:35px">Touch Feedback</h2>
+<h2 id="touch">Touch Feedback</h2>
<p>In the Android L Developer Preview the default touch feedback animations for buttons use the new
<code>RippleDrawable</code> class, which transitions between different states with a ripple
@@ -28,7 +41,7 @@
using the <code>ripple</code> element.</p>
-<h2 style="margin-top:35px">Reveal Effect</h2>
+<h2 id="reveal">Reveal Effect</h2>
<p>The <code>View.createRevealAnimator</code> method enables you to animate a clipping circle
to reveal or hide a view.</p>
@@ -82,7 +95,7 @@
</pre>
-<h2 style="margin-top:35px">Activity Transitions</h2>
+<h2 id="transitions">Activity Transitions</h2>
<p>The Android L Developer Preview enables your app to customize the default animations for
activity transitions. You can specify custom animations for enter and exit transitions and for
@@ -109,10 +122,10 @@
<strong>Figure 1</strong> - A scene transition with one shared element.
</p>
-<h3 style="margin-top:30px">Specify custom transitions</h3>
+<h3>Specify custom transitions</h3>
<p>First, enable window content transitions with the <code>android:windowContentTransitions</code>
-attribute when you define a style that inherits from the Material theme:</p>
+attribute when you define a style that inherits from the material theme:</p>
<pre>
<style name="BaseAppTheme" parent="android:Theme.Material">
@@ -174,14 +187,14 @@
<li><code>Window.setSharedElementExitTransition</code></li>
</ul>
-<h3 style="margin-top:30px">Start an activity using transitions</h3>
+<h3>Start an activity using transitions</h3>
<p>If you enable transitions and set an exit transition for an activity, the transition is activated
when you launch another activity with the <code>startActivity</code> method. If you have set an
enter transition for the second activity, the transition is also activated when the activity
starts.</p>
-<h3 style="margin-top:30px">Shared elements transitions</h3>
+<h3>Shared elements transitions</h3>
<p>To make a screne transition animation between two activities that have a shared element:</p>
@@ -219,7 +232,7 @@
<p>For shared dynamic views that you generate in your code, use the <code>View.setViewName</code>
method to specify a common element name in both activities.</p>
-<h3 style="margin-top:30px">Multiple shared elements</h3>
+<h3>Multiple shared elements</h3>
<p>To make a scene transition animation between two activities that have more than one shared
element, define the shared elements in both layouts with the <code>android:viewName</code>
@@ -237,7 +250,7 @@
</pre>
-<h2 style="margin-top:35px">Curved Motion</h2>
+<h2 id="curvedmotion">Curved Motion</h2>
<p>Animations in material design rely on curves for time interpolation and spatial movement
patterns. The Android L Developer Preview provides new APIs that enable you to define custom
@@ -280,7 +293,7 @@
</pre>
-<h2 style="margin-top:35px">View State Changes</h2>
+<h2 id="viewstate">View State Changes</h2>
<p>The new <code>StateListAnimator</code> class lets you define animators that run when the state
of a view changes. The following example shows how to define an <code>StateListAnimator</code> as
@@ -343,7 +356,7 @@
</pre>
-<h2 style="margin-top:35px">Drawable Tinting</h2>
+<h2 id="drawabletint">Drawable Tinting</h2>
<p>The Android L Developer Preview enables you to define bitmaps as an alpha mask and to tint
them using a color resource or a theme attribute that resolves to a color resource. You can
diff --git a/docs/html/preview/material/compatibility.jd b/docs/html/preview/material/compatibility.jd
index b5555ad..ce04e9e 100644
--- a/docs/html/preview/material/compatibility.jd
+++ b/docs/html/preview/material/compatibility.jd
@@ -2,22 +2,34 @@
@jd:body
-<p>The new material design features (like the Material theme and custom animations) are only
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#materialtheme">Material Theme</a></li>
+ <li><a href="#layouts">Layouts</a></li>
+ <li><a href="#widgets">UI Widgets</a></li>
+ <li><a href="#animation">Animation APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material design features (like the material theme and custom animations) are only
available in the Android L Developer Preview. However, you can design your apps to make use of
these features when running on devices with the Android L Developer Preview and still be
compatible with previous releases of Android.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="materialtheme">Material Theme</h2>
-<p>The Material theme is only available in the Android L Developer Preview. To configure your
-app to use the Material theme on devices running the Android L Developer Preview and an older
+<p>The material theme is only available in the Android L Developer Preview. To configure your
+app to use the material theme on devices running the Android L Developer Preview and an older
theme on devices running earlier versions of Android:</p>
<ol>
<li>Define a theme that inherits from an older theme (like Holo) in
<code>res/values/styles.xml</code>.</li>
-<li>Define a theme with the same name that inherits from the Material theme in
+<li>Define a theme with the same name that inherits from the material theme in
<code>res/values-v21/styles.xml</code>.</li>
<li>Set this theme as your app's theme in the manifest file.</li>
</ol>
@@ -26,7 +38,7 @@
your app will not run on earlier versions of Android.</p>
-<h2 style="margin-top:35px">Layouts</h2>
+<h2 id="layouts">Layouts</h2>
<p>If the layouts that you design according to the material design guidelines do not use any
of the new XML attributes from the Android L Developer Preview, they will work on previous
@@ -38,13 +50,13 @@
Alternative layouts have the same file name.</p>
-<h2 style="margin-top:35px">UI Widgets</h2>
+<h2 id="widgets">UI Widgets</h2>
<p>The <code>RecyclerView</code> and <code>CardView</code> widgets are included in the Android L
Developer Preview Support Library, so they are available in earlier versions of Android.</p>
-<h2 style="margin-top:35px">Animation APIs</h2>
+<h2 id="animation">Animation APIs</h2>
<p>The new APIs for custom animations are only available in the Android L Developer Preview. To
preserve compatibility with earlier verisons of Android, check the system version at runtime before
diff --git a/docs/html/preview/material/get-started.jd b/docs/html/preview/material/get-started.jd
index c527550..9c0e55d 100644
--- a/docs/html/preview/material/get-started.jd
+++ b/docs/html/preview/material/get-started.jd
@@ -2,135 +2,145 @@
@jd:body
-<p>To create material design apps on Android:</p>
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#applytheme">Apply the Material Theme</a></li>
+ <li><a href="#layouts">Design Your Layouts</a></li>
+ <li><a href="#depth">Specify Depth in Your Views</a></li>
+ <li><a href="#widgets">Use the New UI Widgets</a></li>
+ <li><a href="#apis">Use the New APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>To create apps with material design:</p>
<ol>
- <li>Take a look at the <a href="">material design specification</a>.</li>
- <li>Apply the Material <strong>theme</strong> to your app.</li>
- <li>Define additional <strong>styles</strong> to customize the Material theme.</li>
- <li>Create your <strong>layouts</strong> following material design guidelines.</li>
- <li>Specify the <strong>depth</strong> of each component in your layouts to cast appropriate shadows.</li>
- <li>Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
- <li>Use the new <strong>APIs</strong> to customize 3D views and animations in your app.</li>
+ <li style="margin-bottom:10px">
+ Take a look at the <a href="">material design specification</a>.</li>
+ <li style="margin-bottom:10px">
+ Apply the material <strong>theme</strong> to your app.</li>
+ <li style="margin-bottom:10px">
+ Define additional <strong>styles</strong> to customize the material theme.</li>
+ <li style="margin-bottom:10px">
+ Create your <strong>layouts</strong> following material design guidelines.</li>
+ <li style="margin-bottom:10px">
+ Specify the <strong>depth</strong> for views to cast appropriate shadows.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>APIs</strong> to customize the animations in your app.</li>
</ol>
-<h3 style="margin-top:25px;">Update Your App for the Android L Developer Preview</h3>
+<h3>Update Your App for the Android L Developer Preview</h3>
<p>To update an existing app for the Android L Developer Preview, design new layouts following
material design guidelines and consider how you can improve the user experience for your app by
incorporating depth, touch feedback and animations in your UI.</p>
-<h3 style="margin-top:25px;">Create New Apps for the Android L Developer Preview</h3>
+<h3>Create New Apps for the Android L Developer Preview</h3>
<p>If you are creating a new app for the Android L Developer Preview, the material design
guidelines provide you with a solid design framework for your app. Follow these guidelines and
use the new functionality in the Android framework to design and develop your app.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="applytheme">Apply the Material Theme</h2>
-<div style="float:right;margin-left:25px">
-<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
-<p class="img-caption"><strong>Figure 1.</strong> Customizing the Material theme.</p>
-</div>
-
-<p>The new Material theme provides:</p>
-
-<ul>
- <li>System widgets that let you set their color palette</li>
- <li>Touch feedback animations for the system widgets</li>
- <li>Activity transition animations</li>
-</ul>
-
-<p>The Android L Developer Preview lets you easily customize the look of the Material theme
-according to your brand identity with a color palette you control. You can tint the app bar and
-the status bar using theme attributes, as shown in Figure 1.</p>
-
-<p>The system widgets have a new design and touch feedback animations. Activity transitions help
-users navigate your app by providing visual continuity. You can customize the color palette,
-the touch feedback animations, and the activity transitions for your app.</p>
-
-<p>The Material theme is defined as:</p>
-
-<ul>
- <li><code>@android:style/Theme.Material</code> (dark version)</li>
- <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
- <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
-</ul>
-
-<p>For a list of material styles that you can use, see the API reference for
-<code>android.R.styles</code>.</p>
-
-<p class="note">
-<strong>Note:</strong> The Material theme is only available in the Android L Developer Preview.
-For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
-</p>
-
-<h3 style="margin-top:25px;">Theme Inheritance</h3>
-
-<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
-<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
-the theme for the element and any elements inflated below it, which is useful to alter theme
-color palettes in a specific portion of an interface.</p>
-
-<h3 style="margin-top:25px;">Customize the Status Bar</h3>
-
-<p>The Material theme lets you easily customize the status bar, so you can specify a
-color which fits your brand and provides enough contrast to show the white status icons. To
-set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
-you extend the Material theme.</p>
-
-<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
-background), set the <code>android:statusBarColor</code> attribute to
-<code>@android:color/transparent</code>. You can also use the
-<code>Window.setStatusBarColor</code> method for animations or fading.</p>
-
-
-<h2 style="margin-top:35px">Material Design</h2>
-
-<p>In addition to applying the Material theme, you also have to:</p>
-
-<ul>
- <li>Customize the theme's base colors to fit your brand.</li>
- <li>Design your layouts according to material design guidelines.</li>
-</ul>
-
-<p>The Android L Developer Preview provides new attributes to make it easy to customize the
-Material theme:</p>
+<p>To apply the material theme in your app, specify a style that inherits from
+<code>android:theme.Material</code>:</p>
<pre>
+<!-- res/values/styles.xml -->
<resources>
- <!-- inherit from the Material theme -->
- <style name="BaseAppTheme" parent="android:Theme.Material">
- <!-- Main theme colors -->
- <!-- your app's branding color (for the app bar) -->
- <item name="android:colorPrimary">@color/primary</item>
- <!-- darker variant of colorPrimary (for contextual app bars) -->
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
-
- <!-- other theme colors -->
- <item name="android:colorBackground">@color/background</item>
- <item name="android:colorAccent">@color/accent</item>
- <item name="android:colorButtonNormal">@color/button_normal</item>
- <item name="android:colorControlHighlight">@color/button_chigh</item>
- <item name="android:windowBackground">@color/wbackground</item>
+ <!-- your app's theme inherits from the Material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- theme customizations -->
</style>
</resources>
</pre>
-<p>Ensure that you follow material design guidelines when choosing colors for your app.</p>
+<p>The material theme provides new system widgets that let you set their color palette and default
+animations for touch feedback and activity transitions. For more details, see
+<a href="{@docRoot}preview/material/theme.html">Material Theme</a>.</p>
-<p>Design your layouts according to the material design specification. In particular, pay
-attention to:</p>
+
+<h2 id="layouts">Design Your Layouts</h2>
+
+<p>In addition to applying and customizing the material theme, your layouts should conform to
+the material design guidelines. When you design your layouts, pay special attention to the
+following:</p>
<ul>
- <li>Baseline grids</li>
- <li>Keylines</li>
- <li>Spacing</li>
- <li>Touch target size</li>
- <li>Layout structure</li>
+<li>Baseline grids</li>
+<li>Keylines</li>
+<li>Spacing</li>
+<li>Touch target size</li>
+<li>Layout structure</li>
</ul>
<p>You still define layouts inside XML files using the standard tools from the Android framework.
-To specify the depth level of each view in your layout, use the <code>android:elevation</code>
-attribute.</p>
\ No newline at end of file
+For details on the material design guidelines, see the <a href="">material design
+specification</a>.</p>
+
+
+<h2 id="depth">Specify Depth in Your Views</h2>
+
+<p>In the Android L Developer Preview, views can cast shadows. The elevation value of a view
+determines the size of its shadow. To set the elevation of a view, use the
+<code>android:elevation</code> attribute in your layouts:</p>
+
+<pre>
+<Button
+ android:id="@+id/my_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/next"
+ <strong>android:elevation</strong>="10dp" />
+</pre>
+
+<p>For more details, see <a href="{@docRoot}preview/material/views-shadows.html">Views and
+Shadows</a>.</p>
+
+
+<h2 id="widgets">Use the New UI Widgets</h2>
+
+<p>The Android L Developer Preview includes two new UI widgets for complex views,
+<code>RecyclerView</code> and <code>CardView</code>. <code>RecyclerView</code> is a more advanced
+version of <code>ListView</code> that provides performance improvements and is easier to use.
+<code>CardView</code> lets you show pieces of information inside cards with a consistent look
+across apps. To include a <code>CardView</code> in your layout:</p>
+
+<pre>
+<android.support.v7.widget.CardView
+ android:id="@+id/card_view"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ card_view:cardCornerRadius="3dp">
+ ...
+</android.support.v7.widget.CardView>
+</pre>
+
+<p>For more information, see <a href="{@docRoot}preview/material/ui-widgets.html">UI Widgets</a>.</p>
+
+
+<h2 id="apis">Use the APIs to Customize Your Animations</h2>
+
+<p>The Android L Developer Preview includes new APIs to create custom animations in your app.
+For example, you can enable activity transitions and define an exit transition inside an
+activity:</p>
+
+<pre>
+// inside your activity
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+</pre>
+
+<p>When you start another activity from this activity, the exit transition is activated.</p>
+
+<p>To learn about all the features in the new APIs, see <a
+href="{@docRoot}preview/material/animations.html">Animations</a>.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/index.jd b/docs/html/preview/material/index.jd
index 468e4bd..b7abcb4 100644
--- a/docs/html/preview/material/index.jd
+++ b/docs/html/preview/material/index.jd
@@ -19,9 +19,9 @@
</ul>
-<h3 style="margin-top:30px">Material Theme</h3>
+<h3>Material Theme</h3>
-<p>The Material theme provides a new style for your app, system widgets that let you set
+<p>The material theme provides a new style for your app, system widgets that let you set
their color palette, and default animations for touch feedback and activity transitions.</p>
<!-- two columns -->
@@ -42,7 +42,7 @@
</div>
-<h3 style="margin-top:30px">New Widgets</h3>
+<h3>New Widgets</h3>
<p>The Android L Developer Preview includes two new widgets for displaying complex views:</p>
@@ -50,8 +50,8 @@
<div style="width:700px;margin-top:25px;margin-bottom:20px">
<div style="float:left;width:250px;margin-left:40px;margin-right:60px;">
<img src="{@docRoot}preview/material/images/list_mail.png" style="width:250px;"/>
- <p>The new <code>RecyclerView</code> widget is a container for large sets of views that can be
- recycled and scrolled very efficiently.</p>
+ <p>The new <code>RecyclerView</code> widget is a more advanced version of <code>ListView</code>
+ provides performance improvements for dynamic views and is easier to use.</p>
</div>
<div style="float:left;width:250px;margin-right:0px;">
<img src="{@docRoot}preview/material/images/card_travel.png" style="width:250px;"/>
@@ -62,21 +62,13 @@
</div>
-<h3 style="margin-top:30px">3D Views and Shadows</h3>
+<h3>View Shadows</h3>
<p>In addition to the X and Y components, views in the Android L Developer Preview have a Z
component. This new component represents the elevation of a view, which determines the size of
its shadow: views with higher Z values cast bigger shadows.</p>
-
-<h3 style="margin-top:30px">Animations</h3>
-
-<p>The Android L Developer Preview provides new APIs that let you create custom animations for
-touch feedback in UI controls, view state changes, and activity transitions.</p>
-
-<!-- two columns -->
-<div style="width:700px;margin-left:12px;margin-top:25px;margin-bottom:5px">
-<div style="float:left;width:340px;margin-left:0px;margin-right:0px;">
+<div style="width:290px;margin-left:35px;float:right">
<div class="framed-nexus5-port-span-5">
<video class="play-on-hover" autoplay>
<source src="/preview/material/videos/ContactsAnim.mp4"/>
@@ -84,52 +76,41 @@
<source src="/preview/material/videos/ContactsAnim.ogv"/>
</video>
</div>
-</div>
-<div style="float:left;width:340px;margin-right:0px;">
- <div class="framed-nexus5-port-span-5">
- <video class="play-on-hover" autoplay>
- <source src="/preview/material/videos/Dial.mp4"/>
- <source src="/preview/material/videos/Dial.webm"/>
- <source src="/preview/material/videos/Dial.ogv"/>
- </video>
+ <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+ <em>Click on the device screen to replay the movie</em>
</div>
</div>
-<br style="clear:left"/>
-</div>
-<div style="text-align:center;font-size:10pt;margin-right:35px">
-<em>Click on the device screen to replay the movie</em>
-</div>
+<h3>Animations</h3>
-<!-- three columns -->
-<div style="width:700px;margin-top:25px;margin-bottom:0px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Respond to touch events in your views with <strong>touch feedback</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Hide and show views with <strong>reveal effect</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Switch between activities with custom <strong>activity transition</strong> animations.</p>
-</div>
-<br style="clear:left"/>
-</div>
-<!-- three columns -->
-<div style="width:700px;margin-top:0px;margin-bottom:20px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Create custom animation patterns with <strong>curved motion</strong>.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Animate changes in one or more view properties with <strong>view state change</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Show animations in <strong>state list drawables</strong> between view state changes.</p>
-</div>
-<br style="clear:left"/>
-</div>
+<p>The Android L Developer Preview provides new APIs that let you create custom animations for
+touch feedback in UI controls, view state changes, and activity transitions.</p>
+
+<p>The new animation APIs in the Android L Developer Preview let you:</p>
+
+<ul>
+<li style="margin-bottom:15px">
+Respond to touch events in your views with <strong>touch feedback</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Hide and show views with <strong>reveal effect</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Switch between activities with custom <strong>activity transition</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Create custom animation patterns with <strong>curved motion</strong>.
+</li>
+<li style="margin-bottom:15px">
+Animate changes in one or more view properties with <strong>view state change</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Show animations in <strong>state list drawables</strong> between view state changes.
+</li>
+</ul>
-<h3 style="margin-top:30px">New Capabilities for Drawables</h3>
+<h3>New Capabilities for Drawables</h3>
<p>The Android L Developer Preview supports <strong>drawable tinting</strong>: you can define
bitmaps as an alpha mask and tint them using a color resource. You can create these assets only
diff --git a/docs/html/preview/material/theme.jd b/docs/html/preview/material/theme.jd
new file mode 100644
index 0000000..b954960
--- /dev/null
+++ b/docs/html/preview/material/theme.jd
@@ -0,0 +1,100 @@
+page.title=Material Theme
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#colorpalette">Customize the Colot Palette</a></li>
+ <li><a href="#statusbar">Customize the Status Bar</a></li>
+ <li><a href="#inheritance">Theme Inheritance</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material theme provides:</p>
+
+<ul>
+ <li>System widgets that let you set their color palette</li>
+ <li>Touch feedback animations for the system widgets</li>
+ <li>Activity transition animations</li>
+</ul>
+
+<p>The Android L Developer Preview lets you easily customize the look of the material theme
+according to your brand identity with a color palette you control. You can tint the app bar and
+the status bar using theme attributes, as shown in Figure 1.</p>
+
+<div style="float:right;margin-left:25px;margin-top:-25px">
+<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
+<p class="img-caption"><strong>Figure 1.</strong> Customizing the material theme.</p>
+</div>
+
+<p>The system widgets have a new design and touch feedback animations. Activity transitions help
+users navigate your app by providing visual continuity. You can customize the color palette,
+the touch feedback animations, and the activity transitions for your app.</p>
+
+<p>The material theme is defined as:</p>
+
+<ul>
+ <li><code>@android:style/Theme.Material</code> (dark version)</li>
+ <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
+ <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
+</ul>
+
+<p>For a list of material styles that you can use, see the API reference for
+<code>android.R.styles</code>.</p>
+
+<p class="note">
+<strong>Note:</strong> The material theme is only available in the Android L Developer Preview.
+For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
+</p>
+
+
+<h2 id="colorpalette">Customize the Color Palette</h2>
+
+<p>To customize the theme's base colors to fit your brand, define your custom colors using
+theme attributes when you inherit from the material theme:</p>
+
+<pre>
+<resources>
+ <!-- inherit from the material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- Main theme colors -->
+ <!-- your app's branding color (for the app bar) -->
+ <item name="android:colorPrimary">@color/primary</item>
+ <!-- darker variant of colorPrimary (for contextual app bars) -->
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+
+ <!-- other theme colors -->
+ <item name="android:colorButtonNormal">@color/button_normal</item>
+ <item name="android:windowBackground">@color/wbackground</item>
+ </style>
+</resources>
+</pre>
+
+
+<h2 id="statusbar">Customize the Status Bar</h2>
+
+<p>The material theme lets you easily customize the status bar, so you can specify a
+color which fits your brand and provides enough contrast to show the white status icons. To
+set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
+you extend the material theme.</p>
+
+<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
+background), set the <code>android:statusBarColor</code> attribute to
+<code>@android:color/transparent</code>. You can also use the
+<code>Window.setStatusBarColor</code> method for animations or fading.</p>
+
+<p class="note"><strong>Note:</strong>
+The status bar should almost always have a clear delineation from the primary toolbar, except for
+full-bleed imagery cases and when you use a gradient as a protection.
+</p>
+
+
+<h2 id="inheritance">Theme Inheritance</h3>
+
+<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
+<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
+the theme for the element and any elements inflated below it, which is useful to alter theme
+color palettes in a specific portion of an interface.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 5c12a1a..f18bff9 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -2,13 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#recyclerview">RecyclerView</a></li>
+ <li><a href="#cardview">CardView</a></li>
+</ol>
+</div>
+</div>
<p>The support library in the Android L Developer Preview contains two new widgets,
<code>RecyclerView</code> and <code>CardView</code>. Use these widgets to show complex lists
and cards in your app. These widgets have material design styles and animations by default.</p>
-<h2 style="margin-top:35px">RecyclerView</h2>
+<h2 id="recyclerview">RecyclerView</h2>
<p><code>RecyclerView</code> is a more advanced version of <code>ListView</code>. This widget is
a container for large sets of views that can be recycled and scrolled very efficiently. Use the
@@ -62,7 +71,7 @@
<p>To create a custom layout, you extend the <code>RecyclerView.LayoutManager</code> class.</p>
-<h3 style="margin-top:30px">Examples</h3>
+<h3>Examples</h3>
<p>To include a <code>RecyclerView</code> in your layout:</p>
@@ -155,7 +164,7 @@
</pre>
-<h2 style="margin-top:35px">CardView</h2>
+<h2 id="cardview">CardView</h2>
<p><code>CardView</code> extends the <code>FrameLayout</code> class and lets you show information
inside a card with optional rounded corners:</p>
diff --git a/docs/html/preview/material/videos/Dial.mp4 b/docs/html/preview/material/videos/Dial.mp4
deleted file mode 100644
index cd5a6a2..0000000
--- a/docs/html/preview/material/videos/Dial.mp4
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.ogv b/docs/html/preview/material/videos/Dial.ogv
deleted file mode 100644
index b7b29d0..0000000
--- a/docs/html/preview/material/videos/Dial.ogv
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.webm b/docs/html/preview/material/videos/Dial.webm
deleted file mode 100644
index e30d2a5..0000000
--- a/docs/html/preview/material/videos/Dial.webm
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd
index 52fe83c..c5884d6 100644
--- a/docs/html/preview/material/views-shadows.jd
+++ b/docs/html/preview/material/views-shadows.jd
@@ -2,14 +2,24 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#elevation">View Elevation</a></li>
+ <li><a href="#shadows">Shadows and Outlines</a></li>
+ <li><a href="#clip">Clipping Views</a></li>
+</ol>
+</div>
+</div>
-<p>In material design apps, depth has meaning. You should assign higher elevation values to more
+<p>In apps with material design, depth has meaning. You should assign higher elevation values to more
important UI elements in your app. The elevation value of a view determines the size of its
shadow: views with higher Z values cast bigger shadows. Views only cast shadows on the Z=0 plane
under an orthographic projection (the views do not scale for different values of Z).</p>
-<h2 style="margin-top:35px">View Elevation</h2>
+<h2 id="elevation">View Elevation</h2>
<p>The Z value for a view has two components, elevation and translation. The elevation is the
static component, and the translation is used for animations:</p>
@@ -29,7 +39,7 @@
<code>px</code>).</p>
-<h2 style="margin-top:35px">Shadows and Outlines</h2>
+<h2 id="shadows">Shadows and Outlines</h2>
<p>The bounds of a view's background drawable determine the default shape of its shadow. To define
a custom shape for a shadow, such as an oval, use the <code>View.setOutline</code> method:</p>
@@ -55,7 +65,7 @@
<p>To prevent a view from casting a shadow, set its outline to <code>null</code>.</p>
-<h2 style="margin-top:35px">Clipping Views</h2>
+<h2 id="clip">Clipping Views</h2>
<p>The Android L Developer Preview lets you clip a view to its outline area using the
<code>View.setClipToOutline</code> method. Only rectangle, circle, and round rectangle outlines
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index 8f6f8c1..5920ecc 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -19,6 +19,7 @@
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>preview/material/get-started.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>preview/material/theme.html">Material Theme</a></li>
<li><a href="<?cs var:toroot ?>preview/material/ui-widgets.html">UI Widgets</a></li>
<li><a href="<?cs var:toroot ?>preview/material/views-shadows.html">Views and Shadows</a></li>
<li><a href="<?cs var:toroot ?>preview/material/animations.html">Animations</a></li>
@@ -75,7 +76,12 @@
</li>
<li class="nav-section">
<div class="nav-section-header empty">
- <a href="<?cs var:toroot ?>preview/feedback-support.html">Feedback and Support</a>
+ <a href="<?cs var:toroot ?>preview/support.html">Support</a>
</div>
</li>
-</ul>
\ No newline at end of file
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/tos.html">Terms of Service</a>
+ </div>
+ </li>
+</ul>
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
new file mode 100644
index 0000000..67404b6
--- /dev/null
+++ b/docs/html/preview/samples.jd
@@ -0,0 +1,16 @@
+page.title=Samples
+
+@jd:body
+
+<p>The code samples for the L Developer Preview are available in the Android SDK Manager under the
+L Preview section. Here is a summary of everything that is available:</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/setup-devices.jd b/docs/html/preview/setup-devices.jd
index 0654685..86e4845 100644
--- a/docs/html/preview/setup-devices.jd
+++ b/docs/html/preview/setup-devices.jd
@@ -26,12 +26,6 @@
can cause your phone and installed services and applications to stop working.
</p>
-<p><!-- Will this link change before we publish (to a clean version of the doc)?
- Or will we scrub the doc's comments & revision history? -->
-<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
-Preview Terms of Service</a>
-</p>
-
<ol>
<li>Download and extract the Android Developer Preview package to a directory
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
new file mode 100644
index 0000000..23ce6ff
--- /dev/null
+++ b/docs/html/preview/support.jd
@@ -0,0 +1,22 @@
+page.title=Support
+
+@jd:body
+
+<p>If you've encountered bugs or have feedback about the L Developer Preview, create
+an issue on our bug tracker</p>
+
+<p>Go to the Bug Tracker</p>
+
+<h2>Release Notes</h2>
+
+<p>June 25, 2014 - Initial Release of the L Developer Preview</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/tos.jd b/docs/html/preview/tos.jd
new file mode 100644
index 0000000..602439f
--- /dev/null
+++ b/docs/html/preview/tos.jd
@@ -0,0 +1,9 @@
+page.title=License Agreement
+
+@jd:body
+
+<p><!-- Will this link change before we publish (to a clean version of the doc)?
+ Or will we scrub the doc's comments & revision history? -->
+<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
+Preview Terms of Service</a>
+</p>
\ No newline at end of file
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
deleted file mode 100644
index 062968e..0000000
--- a/docs/html/preview/tv/adt-1/index.jd
+++ /dev/null
@@ -1,319 +0,0 @@
-page.title=ADT-1 Developer Kit
-page.tags="emote","e-mote","adt"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#faq">ADT-1 Frequently Asked Questions</a></li>
- <li><a href="#regulatory">Regulatory Disclosures</a></li>
- <li><a href="#safety">Important Safety Instructions</a></li>
- </ol>
-</div>
-</div>
-
-<p class="note">
- <strong>!FIX: FOR REVIEW ONLY:</strong> link to <a href="request.html">ADT-1 Request</a> page.
-</p>
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is provided to a limited number of developers
-who are interested in building new apps or extending their existing apps to run on the Android TV
-platform.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
-</p>
-
-<h2 id="faq">ADT-1 Frequently Asked Questions</h2>
-
-<p>
- <strong>How do I put the gamepad that came with my ADT-1 into pairing mode?</strong>
-</p>
-<p>Press and hold the Back and Home buttons together for about three seconds, until all four
- blue LEDs flash together. When the LEDs are flashing, the gamepad is in pairing mode.</p>
-
-<p>
- <strong>How do I use the gamepad with the on-screen keyboard?</strong>
-</p>
-<p>Use the D-pad or left joystick to move the cursor, and press A to select. Press X to delete a
- character, and press Y to insert a space. Also, you can press the right joystick to toggle caps
- lock, and press the left joystick to show additional symbols.</p>
-
-<p>
- <strong>How do I perform a hard reset of ADT-1?</strong>
-</p>
-<p>Unplug the power cable from the back of ADT-1. Press and hold the small, round button on the
- back of ADT-1 as you re-insert the power cable, and continue to hold the small round button. The
- LED will begin flashing red for a few seconds, then change to multi-color cycle. When the LED
- starts the multi-color cycle, release the small, round button, and ADT-1 will boot. Note: this is
- a factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I do a soft reset?</strong>
-</p>
-<p>Go to Settings - Device - Factory data reset, and select ‘Reset device’. Note: this is a
- factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I turn my device on?</strong>
-</p>
-<p>Plug in the included power cable into the back of ADT-1. There is no on/off switch.</p>
-
-<p>
- <strong>How do I completely turn my device off? </strong>
-</p>
-<p>Unplug in the included power cable from the back of ADT-1. There is no on/off switch.
- However, ADT-1 will begin sleeping (daydream) based on user settings in Display -> Daydream.</p>
-
-<p>
- <strong>How do I connect to the network?</strong>
-</p>
-<p>ADT-1 has both Wi-Fi and Ethernet for connecting to your network. To change your Wi-Fi
- network, go to Settings -> Wi-Fi. To use an Ethernet network connection, simply plug in an
- Ethernet cable (that is connected to your network) into the port on the back of ADT-1.</p>
-
-<p>
- <strong>How do I use the developer cable?</strong>
-</p>
-<p>The developer cable has three connectors: a small, male power connector that plugs into the
- power port on the back of ADT-1, a standard male USB-A connector that connects your PC, and a
- small, female power connector that the included power supply plugs into.</p>
-
-<p>
- <strong>Is there an app for phone and tablet that I can use to control ADT-1?</strong>
-</p>
-<p>Yes, you can download the remote control app from Android phones and tablets here.</p>
-
-<p>
- <strong>Can I connect a USB keyboard/mouse to ADT-1?</strong>
-</p>
-<p>Yes, you can connect a USB keyboard/mouse to the USB port on the back of ADT-1. Note: not all
- manufacturers/models are guaranteed to work.</p>
-<hr />
-<p>
- Press the small, round button on the back of ADT-1 to make it search for Bluetooth devices in
- pairing mode. If multiple accessories are found, press the small, round button to select the
- device you want to pair. Pairing will happen automatically after a few seconds. To pair Bluetooth
- devices to ADT-1 from the UI, go to <strong>Settings > Remote & Accessories >
- Add accessory</strong>.
-</p>
-
-
-
-<h2 id="regulatory">Regulatory Disclosures</h2>
-
-
-<p>Model: W2</p>
-<p>FCC ID: A4R-W2</p>
-<p>IC: 10395A-W2</p>
-<p>U.S. Federal Communications Commission Notices</p>
-<p>To satisfy FCC and IC exposure requirements, a separation distance of at least 20 cm should
- be maintained between the antenna of this device and persons during device operation. Operations
- at closer than this distance are not recommended.</p>
-<p>The antenna used for this transmitter must not be co-located in conjunction with any other
- antenna or transmitter.</p>
-<p>This equipment has been tested and found to comply with the limits for a Class B digital
- device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable
- protection against harmful interference in a residential installation. This equipment generates,
- uses and can radiate radio frequency energy and, if not installed and used in accordance with the
- instructions, may cause harmful interference to radio communications. However, there is no
- guarantee that interference will not occur in a particular installation. If this equipment does
- cause harmful interference to radio or television reception, which can be determined by turning
- the equipment off and on, the user is encouraged to try to correct the interference by one or more
- of the following measures:</p>
-<p>—Reorient or relocate the receiving antenna.</p>
-<p>—Increase the separation between the equipment and receiver.</p>
-<p>—Connect the equipment into an outlet on a circuit different from that to which the receiver
- is connected.</p>
-<p>—Consult the dealer or an experienced radio/ TV technician for help.</p>
-<p>This device complies with part 15 of the FCC Rules. Operation is subject to the following two
- conditions: (1) This device may not cause harmful interference, and (2) this device must accept
- any interference received, including interference that may cause undesired operation.</p>
-<p>Changes or modifications not expressly approved by Google Inc. could void the user's
- authority to operate the equipment.</p>
-<p>Industry Canada Notices</p>
-<p>This device complies with Industry Canada licence-exempt RSS standard(s). Operation is
- subject to the following two conditions: (1) this device may not cause interference, and (2) this
- device must accept any interference, including interference that may cause undesired operation of
- the device.</p>
-<p>Under Industry Canada regulations, this radio transmitter may only operate using an antenna
- of a type and maximum (or lesser) gain approved for the transmitter by Industry Canada. To reduce
- potential radio interference to other users, the antenna type and its gain should be so chosen
- that the equivalent isotropically radiated power (e.i.r.p.) is not more than that necessary for
- successful communication.</p>
-<p>The radiated output power of the Wireless Device is below the Industry Canada (IC) radio
- frequency exposure limits. The Wireless Device should be used in such a manner such that the
- potential for human contact during normal operation is minimized.</p>
-
-<hr />
-
-<p>CAN ICES-3 (B)/NMB-3(B)</p>
-<p>
- <u>Avis d’<em>Industrie Canada</em></u>
-</p>
-<p>
- Le présent appareil est conforme aux <em>CNR</em> d'Industrie Canada applicables aux appareils
- radio exempts de licence. L'exploitation est autorisée aux deux conditions suivantes : (1)
- l'appareil ne doit pas produire de brouillage, et (2) l'appareil doit accepter tout brouillage
- radioélectrique subi, même si le brouillage est susceptible d'en compromettre le fonctionnement.
-</p>
-<p>
- En vertu de la règlementation d’<em>Industrie Canada</em>, cet émetteur radio peut
- fonctionner avec une antenne d'un type et d'un gain maximal (ou inférieur) approuvé pour
- l'émetteur par <em>Industrie Canada</em>. Dans le but de réduire les risques de brouillage
- radioélectrique à l'intention des autres utilisateurs, il faut choisir le type d'antenne et son
- gain de sorte que la puissance isotrope rayonnée équivalente (p.i.r.e.) ne dépasse pas l'intensité
- nécessaire à l'établissement d'une communication satisfaisante.
-</p>
-<p>
- La puissance rayonnée en sortie de l'appareil sans fil est inférieure aux limites fixées par
- <em>Industrie Canada</em> en matière d'exposition aux radiofréquences. L'appareil sans fil
- doit être utilisé de sorte que la possibilité d'un contact humain pendant le fonctionnement
- normal soit limitée.
-</p>
-
-
-<h2 id="safety">Important Safety Instructions</h2>
-
-<p>
- <strong>WARNING:</strong> Read all safety information below before using this device to avoid
- injury.
-</p>
-<ul>
- <li><p>Do not install near heat sources, such as heaters and other devices.</p></li>
- <li><p>Use in a well-ventilated area and plug power adapter into an easily accessible
- outlet. Only use this device with the provided power adapter.</p></li>
- <li><p>The device has no on/off switch. To disconnect from power, you must unplug the
- power adapter.</p></li>
- <li><p>Only use indoors and do not expose to rain, liquid, moisture, excessive heat, or
- naked flame.</p></li>
- <li><p>Clean only with a dry cloth.</p></li>
-</ul>
-<p>
- <strong>WARNING:</strong> Playing video games has been linked to injuries in some
- users. Read all safety and health information below before using the gamepad to avoid possible
- injury.
-</p>
-
-<p><u>Photosensitive Seizures</u></p>
-
-<p>
- A very small percentage of people may experience a seizure when exposed to certain visual images,
- including flashing lights or patterns that may appear in some video games, even people who have no
- history of seizures or epilepsy. These seizures have a variety of symptoms, including
- lightheadedness, altered vision, disorientation, loss of awareness, involuntary movements, loss of
- consciousness, or convulsions. If you experience any of these symptoms, <u>stop gaming
- immediately and consult your doctor</u>.
-</p>
-
-<p><u>Ergonomics</u></p>
-
-<p>Long periods of repetitive motion using incorrect body positioning may be associated with
- physical discomfort and injuries to nerves, tendons, and muscles. If during or after gaming you
- feel pain, numbness, weakness, swelling, burning, cramping, or stiffness,<u>stop gaming
- and consult your doctor</u>.
-
-<p>
- <strong>Healthy Gaming</strong>
-</p>
-
-<p>To reduce risk of seizures or injury, take the following precautions:</p>
-
-<ul>
- <li><p>Sit as far away from the TV screen as possible.</p></li>
- <li><p>Play in a well-lit room.</p></li>
- <li><p>Do not play when you are drowsy or fatigued.</p></li>
- <li><p>Take 10-15 minute breaks every hour if playing video games and avoid prolonged
- gaming.</p></li>
-</ul>
-
-<p>
- <strong>Do Not Attempt Repairs Yourself</strong>
-</p>
-
-<p>There are no user-serviceable parts inside. Do not attempt to open or disassemble.</p>
-
-<p>Failure to follow these safety instructions could result in fire, electric shock, damage to
- the device or other property, or personal injury.</p>
-
-<hr />
-
-<p>
- <strong>Importantes instructions concernant la sécurité</strong>
-</p>
-
-<p>
- <strong>ATTENTION:</strong> Veuillez lire toutes les informations de sécurité énoncées ci-bas
- avant d’utiliser l’appareil pour éviter des blessures.
-</p>
-
-<ul>
- <li><p>Ne pas installer à proximité d’une source de chaleur telle une chaufferette ou un
- autre appareil similaire.</p></li>
- <li><p>Utiliser dans un endroit bien aéré et brancher l’adaptateur électrique dans une
- prise de courant facilement accessible.</p></li>
- <li><p>L’appareil ne possède aucun interrupteur marché/arrêt. Pour mettre l’appareil hors
- tension, il faut débrancher l’appareil de la prise de courant.</p></li>
- <li><p>Utiliser l’appareil uniquement à l’intérieur et ne pas l’exposer à la pluie, à des
- substances liquides, à l’humidité, à la chaleur excessive ou à une flamme.</p></li>
- <li><p>Nettoyer uniquement avec un linge sec.</p></li>
-</ul>
-
-<p>
- <strong>ATTENTION:</strong> Le fait de jouer à des jeux vidéo a été relié à des blessures chez certains
- utilisateurs. Afin d’éviter de possibles blessures, veuillez lire toutes les informations
- concernant la sécurité et la santé énoncées ci-bas avant d’utiliser la tablette de jeu.
-</p>
-
-<p><u>Épilepsie photosensible</u></p>
-
-<p>L’exposition à certaines images visuelles, incluant les lumières ou motifs clignotants qui
- peuvent apparaître dans certains jeux vidéo, peut provoquer chez un très faible pourcentage de
- personnes une crise d’épilepsie, et ce, même si ces personnes n’ont aucun historique de crises ou
- d’épilepsie. Ces crises comportent divers symptômes tels que des étourdissements, une vision
- altérée, un sentiment de désorientation, la perte de conscience, des mouvements involontaires, la
- perte de connaissance ou de conscience ou des convulsions. Si vous ressentez quelconque de ces
- symptômes, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p><u>Ergonomie</u></p>
-
-<p>Les longues périodes de mouvements répétitifs effectués dans une position corporelle
- inadéquate peuvent mener à un inconfort physique et à des blessures aux nerfs, tendons et muscles.
- Si durant ou après avoir joué à des jeux vidéo, vous ressentez de la douleur, de
- l’engourdissement, une faiblesse, de l’inflammation, une sensation de brûlure, des crampes ou de
- la rigidité, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p>
- <strong>Le jeu sécuritaire</strong>
-</p>
-
-<p>Afin de réduire les risques de crises d’épilepsie ou de blessures, veuillez prendre les
- précautions suivantes :</p>
-
-<ul>
- <li>Asseyez-vous aussi loin de l’écran de télévision que possible.</li>
- <li>Jouez dans une pièce munie d’un éclairage adéquat.</li>
- <li>Ne jouez pas lorsque vous êtes étourdi ou fatigué.</li>
- <li>Prenez 10 à 15 minutes de pause après chaque heure de jeu et évitez les périodes de jeu
- prolongées.</li>
-</ul>
-
-<p>
- <strong>Ne pas tenter d’effectuer des réparations par vous-même</strong>
-</p>
-
-<p>L’Appareil ne contient aucune pièce pouvant être réparée par l’utilisateur. Ne pas tenter
- d’ouvrir ou de désassembler l’Appareil.</p>
-
-<p>Le défaut de suivre ces instructions de sécurité pourrait provoquer un feu, un choc
- électrique, un dommage à l’Appareil ou à d’autres objets ou des lésions corporelles.</p>
diff --git a/docs/html/preview/tv/adt-1/request.jd b/docs/html/preview/tv/adt-1/request.jd
deleted file mode 100644
index 69e3e4e..0000000
--- a/docs/html/preview/tv/adt-1/request.jd
+++ /dev/null
@@ -1,47 +0,0 @@
-page.title=Request ADT-1 Developer Kit
-
-@jd:body
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is offered to developers who are interested in
-building new apps or extending their existing apps to run on the Android TV platform before
-the commercial release of Android TV devices. Supplies of the ADT-1 kit are limited and
-requesting one not guarantee it will be delivered to you.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
- For more information about the ADT-1 kit, see the
- <a href="{@docRoot}preview/tv/adt-1/index.html">ADT-1 Developer Kit</a> information page.
-</p>
-
-<div class="sdk-terms" style="width:678px" onfocus="this.blur()">
-<div class="sdk-terms-padding">
-Android ADT-1 Developer Kit Request Agreement.
-
-1. These are the terms.
-
-2. You must agree to the terms.
-
-3. Otherwise you have not agreed to the terms.
-
-4. And then we don't want to talk to you.
-</div>
-</div>
-
-
-<p class="caution">
- <strong>Important:</strong> The email address your provide in this form is used to verify
- you as an Android developer. Please provide a Google account email address that is associated
- with the Google Play app you enter. We may also use your email address to provide you with
- updates about the ADT-1 Developer Kit and Android TV.
-</p>
-
-<iframe src="https://docs.google.com/a/google.com/forms/d/1MLhC39rf3aAJw-KhZw9cyjT1dWuz_k3_iC5QXpC4Cbw/viewform?embedded=true"
- width="100%" height="540" frameborder="0" marginheight="0" marginwidth="0"
- id="signupform">Loading...</iframe>
-
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
deleted file mode 100644
index b9de3a4..0000000
--- a/docs/html/preview/tv/games/index.jd
+++ /dev/null
@@ -1,70 +0,0 @@
-page.title=Games on TV
-page.tags="controller"
-
-@jd:body
-
-<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
-
-<h2>Overview</h2>
-<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
-<ul>
-<li>Display</li>
-<li>Control</li>
-<li>Manifest</li>
-<li>Google Play Game Services</li>
-<li>Web</li>
-</ul>
-<h2>Display</h2>
-<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
-<h3>A shared display</h3>
-<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
-<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
-<ul>
-<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
-<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
-</ul>
-<h3>No touch interface</h3>
-<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
-<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
-<h3>Landscape display</h3>
-<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
-<a id=control><h2>Control</h2>
-<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
-<h3>D-pad</h3>
-<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
-<p>The player needs to use the gamepad in all aspects of the game–not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
-<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience–and penalize your game in the ratings.</p>
-<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
-<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
-<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
-<h3>Back-button behavior</h3>
-<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play > Game pause screen > Game main screen > Android home screen.</p>
-<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
-<h3>Handling multiple controllers</h3>
-<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
-<h3>Handling disconnects</h3>
-<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
-<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
-<h2>Manifest</h2>
-<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
-<pre class="fragment"><application>
- . . .
- <meta-data android:name="isGame" android:value=["true" | "false"]/>
-android:isGame=["true" | "false"] >
- . . .
-</application>
-</pre><h2>Google Play Game Services</h2>
-<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
-<h3>Achievements</h3>
-<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
-<h3>Sign-on</h3>
-<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
-<h3>Saving</h3>
-<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
-<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
-<h3>Multiplayer experience</h3>
-<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
-<h2>Web</h2>
-<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
-<p>Webviews will work for logins to services like Google+ and Facebook. </p>
-
diff --git a/docs/html/preview/tv/images/atv.png b/docs/html/preview/tv/images/atv.png
deleted file mode 100644
index cd96164..0000000
--- a/docs/html/preview/tv/images/atv.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/browsefragment.png b/docs/html/preview/tv/images/browsefragment.png
deleted file mode 100644
index 8998b13..0000000
--- a/docs/html/preview/tv/images/browsefragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/detailsfragment.png b/docs/html/preview/tv/images/detailsfragment.png
deleted file mode 100644
index 014ab23..0000000
--- a/docs/html/preview/tv/images/detailsfragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png
deleted file mode 100644
index 2c18827..0000000
--- a/docs/html/preview/tv/images/home-recommendations.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd
deleted file mode 100644
index da40985..0000000
--- a/docs/html/preview/tv/index.jd
+++ /dev/null
@@ -1,12 +0,0 @@
-page.title=Android on TV
-
-@jd:body
-
-<img src="{@docRoot}preview/tv/images/atv.png" align="middle"/>
-
-<p>Android offers a rich user experience that's optimized for apps running on large screen
- devices, such as high-definition televisions. Apps on TV offer new opportunities to
- delight your users from the comfort of their couch.</p>
-
-<a href="{@docRoot}preview/tv/start/index.html">Get Started ></a>
-
diff --git a/docs/html/preview/tv/start/hardware-features.jd b/docs/html/preview/tv/start/hardware-features.jd
deleted file mode 100644
index f3b51bb..0000000
--- a/docs/html/preview/tv/start/hardware-features.jd
+++ /dev/null
@@ -1,174 +0,0 @@
-page.title=Hardware Features on TV
-page.tags="unsupported"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#unsupported-features">Unsupported Hardware Features</a></li>
- <li><a href="#workaround-features">Handling Unsupported Features</a></li>
- <li><a href="#check-features">Checking Available Features</a>
- <ol>
- <li><a href="#no-touchscreen">Touch screen</a></li>
- <li><a href="#no-camera">Camera</a></li>
- <li><a href="#no-gps">GPS</a></li>
- </ol>
-
- </li>
- </ol>
-</div>
-</div>
-
-<p>TVs do not have some of the hardware features found on other Android devices.
-Touch screens, cameras and GPS receivers are some of the most commonly used hardware features
-which are typically not available on a TV. When you build an app for TV, you must carefully
-consider if your app can handle not having these features and, if necessary, work around them.</p>
-
-<p>This guide discusses the hardware features not available on TV devices and shows you how to
-work around those limitations in your app.</p>
-
-
-<h2 id="unsupported-features">Unsupported Hardware Features</h2>
-
-<p>TVs have a different purpose from other devices, and so they do not have hardware
-features that other Android-powered devices often have. For this reason, the Android system
-does not support the following features for a TV device:
-
-<table>
-<tr>
-<th>Hardware</th>
-<th>Android feature descriptor</th>
-</tr>
-<tr>
-<td>Camera</td>
-<td>android.hardware.camera</td>
-</tr>
-<tr>
-<td>GPS</td>
-<td>android.hardware.location.gps</td>
-</tr>
-<tr>
-<td>Microphone</td>
-<td>android.hardware.microphone</td>
-</tr>
-<tr>
-<td>Near Field Communications (NFC)</td>
-<td>android.hardware.nfc</td>
-</tr>
-<tr>
-<td>Telephony</td>
-<td>android.hardware.telephony</td>
-</tr>
-<tr>
-<td>Touchscreen</td>
-<td>android.hardware.touchscreen</td>
-</tr>
-</table>
-</p>
-
-
-<h2 id="check-features">Checking Available Features</h2>
-
-<p>To check if a feature is available at runtime, call {@link
- android.content.pm.PackageManager#hasSystemFeature(String)}. This method takes a single string
- argument that specifies the feature you want to check. For example, to check for a touch screen,
- use {@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
- {@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.</p>
-
-<p>The following code example demonstrates how to detect the availability of a hardware features
- at runtime:</p>
-
-<pre>
-// Check if the telephony hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
- Log.d("Mobile Test", "Running on phone");
-// Check if android.hardware.touchscreen feature is available.
-} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
- Log.d("Tablet Test", "Running on devices that don't support telephony but "+
- "do have a touch screen.");
-} else {
- Log.d("TV Test", "Running on a TV!");
-}
-</pre>
-
-
-<h2 id="workaround-features">Handling Unsupported Features</h2>
-
-<p>Depending on the design and functionality of your app, you may be able to work around certain
- hardware features being unavailable. This section discusses how to workaround specific hardware
- features.</p>
-
-
-<h3 id="no-touchscreen">Touch screen</h3>
-
-<p>Android doesn't support touch screen interaction for TV devices, since most TVs don't have touch
- screens, and using a touch screen is not consistent with a viewing environment where the user is
- seated 10 feet away from the display.</p>
-
-<p>On TV devices, you should workaround this limitation by supporting navigation using a directional
- pad (D-pad) on TV remote control. For more information on properly supporting navigation using
- TV-friendly controls, see <a href="{@docRoot}preview/tv/ui/navigation.html">Navigation for
- TV</a>.</p>
-
-<p>You can explicitly declare if your application requires (or does not require) a touch screen
- by including the following entry in your manifest:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.touchscreen"
- android:required="false"/>
-</pre>
-
-
-<h3 id="no-camera">Camera</h3>
-
-<p>Although a TV typically does not have a camera, you can still provide a photography-related
- application on a TV. For example, if you have an app that takes, views and edits photos, you can
- disable its picture-taking functionality for TVs and still allow users to view and even edit
- photos. If you decide that you want to enable your camera-related application to work on a
- TV device without a camera, you can add an attribute to your app manifest declaring that
- a camera is not required by your app:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.camera" android:required="false" />
-</pre>
-
-<p>If you enable your application to run without a camera, you should add code to your application
-that detects if the camera feature is available and make adjustments to the operation of your app.
-The following code example demonstrates how to detect the presence of a camera:</p>
-
-<pre>
-// Check if the camera hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
- Log.d("Camera test", "Camera available!");
-} else {
- Log.d("Camera test", "No camera available. View and edit features only.");
-}
-</pre>
-
-
-<h3 id="no-gps">GPS</h3>
-
-<p>TVs are stationary, indoor devices, and do not have built-in global positioning system (GPS)
- receivers. If your application uses location information, you can still allow users to search
- for a location or use a static location provider such as a zip code configured during the
- TV device setup.</p>
-
-<pre>
-LocationManager locationManager = (LocationManager) this.getSystemService(
- Context.LOCATION_SERVICE);
-Location location = locationManager.getLastKnownLocation("static");
-Geocoder geocoder = new Geocoder(this);
-Address address = null;
-
-try {
- address = geocoder.getFromLocation(location.getLatitude(),
- location.getLongitude(), 1).get(0);
- Log.d("Zip code", address.getPostalCode());
-
-} catch (IOException e) {
- Log.e(TAG, "Geocoder error", e);
-}
-</pre>
-
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
deleted file mode 100644
index 7f726bd..0000000
--- a/docs/html/preview/tv/start/index.jd
+++ /dev/null
@@ -1,193 +0,0 @@
-page.title=Get Started with TV Apps
-page.tags="leanback","recyclerview","launcher"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#prerequisites">Prerequisites</a></li>
- <li><a href="#dev-project">Setup a TV Project</a>
- <ul>
- <li><a href="#tv-activity">Create a TV Activity</a></li>
- <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
- </ul>
- </li>
- <li><a href="#build-it">Build TV Apps</a></li>
- </ol>
-</div>
-</div>
-
-<p>This guide describes how to prepare your development environment and projects for building
- TV apps, including updating your existing app to run on TV devices.</p>
-
-
-<h2 id="prerequisites">Prerequisites</h2>
-
-<p>Before you begin setting up to build apps for TV, you must:</p>
-
-<ul>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html">
- Setup the Preview SDK</a></strong>
- <br>
- The preview SDK provides the developer tools needed to build and test apps for TV.
- </li>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html#project">
- Create a Preview SDK Project</a></strong>
- <br>
- In order to access new APIs for TV devices, you must create a project that targets the preview
- release level or modify an existing project to target the preview release.
- </li>
-</ul>
-
-
-<h2 id="dev-project">Setup a TV Project</h2>
-
-<p>TV apps use the same structure as those for phones and tablets. This means you can modify
- your existing apps to also run on TV devices or create new apps based on what you already know
- about building apps for Android. This section discusses how to modify an existing app or create a
- new app that runs on TV devices.</p>
-
-<p>There are the main steps to creating an app that runs on TV devices, only the first of these
- is required:</p>
-
-<ul>
- <li><strong>Activity for TV</strong> - (Required) Your application must have an activity that is
- is declared to run on TV devices through an app manifest entry.</li>
- <li><strong>TV Support Libraries</strong> - (Optional) There are several Support Libraries
- available for TV devices that provide user interface widgets for building user interfaces
- for use on TV.</li>
-</ul>
-
-
-<h3 id="tv-activity">Create a TV Activity</h3>
-
-<p>Applications that are intended to run on TV devices must declare a launcher activity for TV
- in their manifest using a the {@code android.intent.category.LEANBACK_LAUNCHER} intent filter.
- This filter identifies your app as being built for TV, enabling your app to be displayed in the
- Google Play app running on TV devices. Declaring this intent also identifies which activity in
- your app should be launched when a user selects your app icon on a TV.</p>
-
-<p class="caution">
- <strong>Caution:</strong> If you do not include the LEANBACK_LAUNCHER intent filter in your app,
- it will not be visible to users running the Google Play store on TV devices. If your load an app
- without this intent filter onto a TV device using developer tools, the app does not appear in
- the TV user interface.
-</p>
-
-<p>The following code snippet shows how to include this intent filter in your manifest:</p>
-
-<pre>
-<application>
- ...
- <activity
- android:name="com.example.android.MainActivity"
- android:label="@string/app_name" >
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <activity
- android:name="com.example.android.<strong>TvActivity</strong>"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.NoTitleBar">
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="<strong>android.intent.category.LEANBACK_LAUNCHER</strong>" />
- </intent-filter>
-
- </activity>
-</application>
-</pre>
-
-<p>The second activity manifest entry in the example above specifies that it should be used as
- the main activity when your app launched on an TV device.</p>
-
-<p>If you have an existing app that you are modifying for TV use, your app should not use the same
- activity layout for TV that you do for phones and tablets. The user interface of your TV app (or
- TV portion of your existing app) should provide a simpler interface that can be easily navigated
- using a remote control from a couch. For guidelines on designing an app for TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide. For more instructions on
- developing a user interface that is appropriate to TV, see the
- <a href="{@docRoot}preview/tv/ui/index.html">TV User Interface</a> guide.
-</p>
-
-
-<h3 id="tv-libraries">Add TV Support Libraries</h3>
-
-<p>The Preview SDK includes support libraries that are intended for use with TV apps. These
- libraries provide APIs and user interface widgets for use on TV devices. The libraries are
- located in the {@code <sdk>/extras/android/support/} directory where you installed the
- Preview SDK. Here is a list of the libraries and their general purpose:</p>
-
-<ul>
- <li><strong>v17 leanback library</strong> - Provides user interface widgets for TV, including
- {@code BrowseFragment}, {@code DetailsFragment}, and {@code SearchFragment}.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v17/leanback}</li>
- <li>Gradle dependency: {@code com.android.support:leanback-v17:20.0.+}</li>
- <li>Contains resources: Yes</li>
- </ul>
- </li>
- <li><strong>v7 recyclerview library</strong> - Provides classes for managing display of long
- lists in a memory efficient manner. Several classes in the v17 leanback library depend on the
- classes in this library.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v7/recyclerview}</li>
- <li>Gradle dependency: {@code com.android.support:recyclerview-v7:20.0.+}</li>
- <li>Contains resources: No</li>
- </ul>
- </li>
-</ul>
-
-<p class="note">
- <strong>Note:</strong> You are not required to use these support libraries for your TV app.
- However, we strongly recommend using them, particularly for apps that provide a media catalog
- browsing interface.
-</p>
-
-<p>If you decide to use the v17 leanback library for your application, you should note that it is
- dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
- appcompat library</a>, which is, in turn, dependent on the
- <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
- that apps that use the leanback support library should include all of these support
- libraries:</p>
-
-<ul>
- <li>v17 leanback support library</li>
- <li>v7 recyclerview support library</li>
- <li>v7 appcompat support library</li>
- <li>v4 support library</li>
-</ul>
-
-<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
- you to take specific steps to include them in app projects. For instructions on
- importing a support library with resources, see
- <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
- Support Library Setup</a>.
-</p>
-
-
-<h2 id="build-it">Build TV Apps</h2>
-
-<p>After you have completed the steps described above, it's time to start building apps for
- the big screen! Check out these additional topics to help you build your app for TV:
-
-<ul>
- <li><a href="{@docRoot}preview/tv/ui/index.html">User Interface</a> - The user interface of
- TV devices is different from those of other Android devices. See this topic to find out how
- to build TV user interfaces and the widgets provided to make it easier to build them.
- </li>
- <li><a href="{@docRoot}preview/tv/games/index.html">Games for TV</a> - TV devices are great
- platforms for games. See this topic for information on building great game experiences for
- TV.</li>
- <li><a href="{@docRoot}preview/tv/start/hardware-features.html">Hardware features</a> - TV
- devices do not contain hardware features normally found on other Android devices. See this
- topic for information on unsupported hardware features and what to do about them.
- </li>
-</ul>
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
deleted file mode 100644
index 9d878b0..0000000
--- a/docs/html/preview/tv/ui/browse.jd
+++ /dev/null
@@ -1,217 +0,0 @@
-page.title=BrowseFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=DetailsFragment
-next.link=details.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#layout">Media Browse Layout</a></li>
- <li><a href="#lists">Displaying Media Lists</a></li>
- <li><a href="#background">Updating the Background</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>The <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- provides several APIs for displaying and browsing media catalogs
- on the TV devices. This guide discusses how to use the classes provided by this library to
- implement a user interface for browsing music or videos from your app's media catalog.</p>
-
-
-<h2 id="layout">Media Browse Layout</h2>
-
-<p>The BrowseFragment class in the Leanback support library allows you to create a primary
- layout for browsing categories and rows of media items with a minimum of code. The following
- example layout shows how to create a layout that contains a BrowseFragment:</p>
-
-<pre>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <fragment
- <strong>android:name="android.support.v17.leanback.app.BrowseFragment"</strong>
- android:id="@+id/browse_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-</LinearLayout>
-</pre>
-
-<p>In order to work with this layout in an activity, retrieve the BrowseFragment element from
- the layout. Use the BrowseFragment.Params class to set display parameters such as the icon, title
- and whether category headers are enabled. The following code sample demonstrates how to set the
- layout parameters for a BrowseFragment in a layout:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
-
- public static final String TAG ="BrowseActivity";
-
- protected BrowseFragment mBrowseFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_fragment);
-
- final FragmentManager fragmentManager = getFragmentManager();
- <strong>mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
- R.id.browse_fragment);</strong>
-
- // Set display parameters for the BrowseFragment
- BrowseFragment.Params params = mBrowseFragment.getBrowseParams();
- if (params == null) {
- params = new BrowseFragment.Params();
- }
- params.setHeadersState(BrowseFragment.HEADERS_ENABLED);
- params.setTitle(getString(R.string.app_name));
- params.setBadgeImage(getResources().getDrawable(R.drawable.ic_launcher));
- mBrowseFragment.setBrowseParams(params);
-
- }
-}
-</pre>
-
-
-<h2 id="lists">Displaying Media Lists</h2>
-
-<p>The BrowseFragment allows you to define and display browseable media content categories and
- media items from a media catalog using adapters and presenters. Adapters enable you to connect to
- local or online data sources that contain your media catalog information. Presenter classes hold
- data about media items and provide layout information for displaying an item on screen.</p>
-
-<p>The following example code shows an implementation of a presenter for displaying string
- data:</p>
-
-<pre>
-public class StringPresenter extends Presenter {
- private static final String TAG = "StringPresenter";
-
- public ViewHolder onCreateViewHolder(ViewGroup parent) {
- TextView textView = new TextView(parent.getContext());
- textView.setFocusable(true);
- textView.setFocusableInTouchMode(true);
- textView.setBackground(
- parent.getContext().getResources().getDrawable(R.drawable.text_bg));
- return new ViewHolder(textView);
- }
-
- public void onBindViewHolder(ViewHolder viewHolder, Object item) {
- ((TextView) viewHolder.view).setText(item.toString());
- }
-
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- Log.d(TAG, "onUnbindViewHolder");
- }
-}
-</pre>
-
-<p>Once you have constructed a presenter class for your media items, you can build and attach an
- adapter to the BrowseFragment to display those items on screen for browsing by the user. The
- following example code demonstrates how to construct an adapter to display categories and items
- in those categories using the StringPresenter class shown in the previous code example:</p>
-
-<pre>
-private ArrayObjectAdapter mRowsAdapter;
-private static final int NUM_ROWS = 4;
-
-@Override
-protected void onCreate(Bundle savedInstanceState) {
- ...
-
- buildRowsAdapter();
-}
-
-private void buildRowsAdapter() {
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
-
- for (int i = 0; i < NUM_ROWS; ++i) {
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(i, "Category " + i, null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
- }
-
- mBrowseFragment.setAdapter(mRowsAdapter);
-}
-</pre>
-
-<p>This example shows a static implementation of the adapters. A typical media browsing
- application uses data from an online database or web service. For an example of a browsing
- application that uses data retrieved from the web, see the <a href="">!FIX</a> code sample.</p>
-
-<p>The following screenshot shows the output of this code on an Android TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/browsefragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on the
- {@link android.support.v17.leanback.app.BrowseFragment} and {@code StringPresenter}
- classes.
-</p>
-
-
-<h2 id="background">Updating the Background</h2>
-
-<p>In order to add visual interest to a media browsing app on TV, you can update the background
- image as users browse through content. This technique can make interaction with your app feel more
- cinematic and enjoyable for users.</p>
-
-<p>The Leanback support library provides a {@link
- android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
- app activity. The following example shows how to create a simple method for updating the
- background:</p>
-
-<pre>
-protected void updateBackground(Drawable drawable) {
- BackgroundManager.getInstance(this).setDrawable(drawable);
-}
-</pre>
-
-<p>Many of the existing media browse apps automatically update the background as the user
- navigates through media listings. In order to do this, you can set up a selection listener to
- automatically update the background based on the user's current selection. The following example
- shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
- class to catch selection events and update the background:</p>
-
-<pre>
-protected void clearBackground() {
- BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
-}
-
-protected OnItemSelectedListener getDefaultItemSelectedListener() {
- return new OnItemSelectedListener() {
- @Override
- public void onItemSelected(Object item, Row row) {
- if (item instanceof Movie ) {
- URI uri = ((Movie)item).getBackdropURI();
- updateBackground(uri);
- } else {
- clearBackground();
- }
- }
- };
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> The implementation above is a simple example shown for purposes of
- illustration. When creating this function in your own app, you should consider running the
- background update action in a separate thread for better performance. In addition, if you are
- planning on updating the background in response to user's scrolling through items, consider adding
- a time to delay a background image update until the user settles on item, to avoid excessive
- background image updates.
-</p>
diff --git a/docs/html/preview/tv/ui/details.jd b/docs/html/preview/tv/ui/details.jd
deleted file mode 100644
index 7da3b5d..0000000
--- a/docs/html/preview/tv/ui/details.jd
+++ /dev/null
@@ -1,230 +0,0 @@
-page.title=DetailFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=BrowseFragment
-previous.link=browse.html
-next.title=Searching in TV Apps
-next.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#details-presenter">Build a Details Presenter</a></li>
- <li><a href="#details-fragment">Extend the Details Fragment</a>
- <li><a href="#activity">Creating a Details Activity</a></li>
- <li><a href="#item-listener">Listener for Clicked Items</a></li>
- </li>
- </ol>
-</div>
-</div>
-
-<p>The media browsing interface classes provided by the
- <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- include classes for displaying additional information about a media item, such as a description
- or reviews, and taking action on that item, such as purchasing it or playing its content. This
- section discusses how to create a presenter class for media item details and extend the
- {@code DetailsFragment} class to implement a details view for a media item when it
- is selected by a user.
-</p>
-
-<p class="note">
- <strong>Note:</strong> The implementation example shown here uses an additional activity to
- contain the {@code DetailsFragment}. However, it is possible to avoid creating a second activity
- by replacing the current {@code BrowseFragment} with a {@code DetailsFragment} within the <em>same</em>
- activity using fragment transactions. For more information on using fragment transactions, see the
- <a href="{@docRoot}training/basics/fragments/fragment-ui.html#Replace">Building a Dynamic
- UI with Fragments</a> training.
-</p>
-
-
-<h2 id="details-presenter">Build a Details Presenter</h2>
-
-<p>In the media browsing framework provided for by the leanback support library, you use
- presenter objects to control the display of data on screen, including media item details. The
- framework provides the {@code AbstractDetailsDescriptionPresenter} class for this purpose, which
- is a nearly complete implementation of the presenter for media item details. All you have to do is
- implement the {@code onBindDescription()} method to bind the view fields to your data objects, as shown in
- the following code sample:</p>
-
-<pre>
-public class DetailsDescriptionPresenter
- extends AbstractDetailsDescriptionPresenter {
-
- @Override
- protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
- MyMediaItemDetails details = (MyMediaItemDetails) itemData;
- // In a production app, the itemData object contains the information
- // needed to display details for the media item:
- // viewHolder.getTitle().setText(details.getShortTitle());
-
- // Here we provide static data for testing purposes:
- viewHolder.getTitle().setText(itemData.toString());
- viewHolder.getSubtitle().setText("2014 Drama TV-14");
- viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
- + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
- + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
- + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
- + "commodo consequat.");
- }
-}
-</pre>
-
-
-<h2 id="details-fragment">Extend the Details Fragment</h2>
-
-<p>When you use the {@code DetailsFragment} class for displaying your media item details, you
- extend that class to provide additional content such as a preview image and actions for the media
- item. You can also provide additional content, such as a list of related media items.</p>
-
-<p>The following example code demonstrates how to use the presenter class you created in the
- previous section, add a preview image and actions for the media item being viewed. This example
- also shows the addition of a related media items row, which appears below the details listing.</p>
-
-<pre>
-public class MediaItemDetailsFragment extends DetailsFragment {
- private static final String TAG = "MediaItemDetailsFragment";
- private ArrayObjectAdapter mRowsAdapter;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log.i(TAG, "onCreate");
- super.onCreate(savedInstanceState);
-
- buildDetails();
- }
-
- private void buildDetails() {
- ClassPresenterSelector selector = new ClassPresenterSelector();
- // Attach your media item details presenter to the row presenter:
- DetailsOverviewRowPresenter rowPresenter =
- new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
-
- selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
- selector.addClassPresenter(ListRow.class,
- new ListRowPresenter());
- mRowsAdapter = new ArrayObjectAdapter(selector);
-
- Resources res = getActivity().getResources();
- DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
- "Media Item Details");
-
- // Add images and action buttons to the details view
- detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
- detailsOverview.addAction(new Action(1, "Buy $9.99"));
- detailsOverview.addAction(new Action(2, "Rent $2.99"));
- mRowsAdapter.add(detailsOverview);
-
- // Add a Related items row
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(0, "Related Items", null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
-
- setAdapter(mRowsAdapter);
- }
-}
-</pre>
-
-<p>The following screenshot shows the output of this code on a TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/detailsfragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on {@code DetailsFragment}, using a
- {@code DetailsOverviewRow} and a {@code ListRow} for related items.
-</p>
-
-
-<h3 id="activity">Creating a Details Activity</h3>
-
-<p>Fragments such as the {@code DetailsFragment} must be contained within an activity in order
- to be used for display. Creating an activity for your details view, separate from the browse
- activity, enables you to invoke your details view using an Intent. This section explains how to
- build an activity to contain your implementation of the detail view for your media items.</p>
-
-<p>Start creating the details activity by building a layout that references your implementation
- of the {@code DetailsFragment}:</p>
-
-<pre>
-<!-- file: res/layout/details.xml -->
-
-<fragment xmlns:android="http://schemas.android.com/apk/res/android"
- <strong>android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"</strong>
- android:id="@+id/details_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
-/>
-</pre>
-
-<p>Next, create an activity class that uses the layout shown in the previous code example:</p>
-
-<pre>
-public class DetailsActivity extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- <strong>setContentView(R.layout.details);</strong>
- }
-}
-</pre>
-
-<p>Finally, add this new activity to the manifest. Remember to apply the Leanback theme to
- ensure that the user interface is consistent with the media browse activity:</p>
-
-<pre>
-<application>
- ...
-
- <activity android:name=".DetailsActivity"
- android:exported="true"
- <strong>android:theme="@style/Theme.Leanback"/></strong>
-
-</application>
-</pre>
-
-
-<h3 id="item-listener">Listener for Clicked Items</h3>
-
-<p>After you have implemented the {@code DetailsFragment}, you must modify your main media
- browsing view to move to your details view when a user clicks on a media item. In order to enable
- this behavior, add an {@code OnItemClickedListener} object to the BrowseFragment that fires an
- intent to start the item details activity.</p>
-
-<p>The following example shows how to implement a listener to start the details view when a user
- clicks a media item in the main media browsing activity:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
- ...
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ...
-
- // create the media item rows
- buildRowsAdapter();
-
- // add a listener for selected items
- mBrowseFragment.setOnItemClickedListener(
- new OnItemClickedListener() {
- @Override
- public void onItemClicked(Object item, Row row) {
- System.out.println("Media Item clicked: " + item.toString());
- Intent intent = new Intent(BrowseMediaActivity.this,
- DetailsActivity.class);
- // pass the item information
- intent.getExtras().putLong("id", item.getId());
- startActivity(intent);
- }
- });
- }
-}
-</pre>
diff --git a/docs/html/preview/tv/ui/in-app-search.jd b/docs/html/preview/tv/ui/in-app-search.jd
deleted file mode 100644
index b372254..0000000
--- a/docs/html/preview/tv/ui/in-app-search.jd
+++ /dev/null
@@ -1,119 +0,0 @@
-page.title=Searching in TV Apps
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=DetailsFragment
-previous.link=details.html
-next.title=Recommendations
-next.link=recommendations.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#add-search-ui">Add Search User Interface</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Users frequently have specific content in mind when using a media app. A search interface can
- help your users get to the content they want faster than browsing. The Leanback library provides a
- set of classes to enable a standard search interface within your app that is consistent with other
- search functions on TV and provides features such as voice input.</p>
-
-<h2 id="add-search-ui">Add Search User Interface</h2>
-<p>When you use the BrowseFragment class for your media browsing interface, you can enable the
- search icon by setting an OnClickListener to the browse fragment object. The following sample code
- demonstrates this technique.</p>
-
-<pre>
-@Override
-public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_activity);
-
- mBrowseFragment = (BrowseFragment)
- getFragmentManager().findFragmentById(R.id.browse_fragment);
-
- ...
-
- mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
- startActivity(intent);
- }
- });
-
- mBrowseFragment.setAdapter(buildAdapter());
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> You can set the color of the search icon using the
- {@code setSearchAffordanceColor()} method of {@code BrowseFragment}.
-</p>
-
-<p>When a user selects the search icon, the system invokes a search activity via the defined
- Intent. Your search activity should use a linear layout containing a SearchFragment. This fragment
- must also implement the SearchFragment.SearchResultProvider interface in order to display the
- results of a search. The following code sample shows how to extend the SearchFragment class to
- provide a search interface and results:</p>
-
-<pre>
-public class MySearchFragment extends SearchFragment
- implements SearchFragment.SearchResultProvider {
-
- private static final int SEARCH_DELAY_MS = 300;
- private ArrayObjectAdapter mRowsAdapter;
- private Handler mHandler = new Handler();
- private SearchRunnable mDelayedLoad;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
- setSearchResultProvider(this);
- setOnItemClickedListener(getDefaultItemClickedListener());
- mDelayedLoad = new SearchRunnable();
- }
-
- @Override
- public ObjectAdapter getResultsAdapter() {
- return mRowsAdapter;
- }
-
- @Override
- public boolean onQueryTextChange(String newQuery) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(newQuery)) {
- mDelayedLoad.setSearchQuery(newQuery);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(query)) {
- mDelayedLoad.setSearchQuery(query);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-}
-</pre>
-
-<p>This example code shown above is meant to be used with a separate SearchRunnable class, that
- runs the search query on a separate thread. This technique keeps potentially slow-running queries
- from interfering with the main user interface thread.</p>
-
diff --git a/docs/html/preview/tv/ui/index.jd b/docs/html/preview/tv/ui/index.jd
deleted file mode 100644
index 9513bc6..0000000
--- a/docs/html/preview/tv/ui/index.jd
+++ /dev/null
@@ -1,44 +0,0 @@
-page.title=User Interfaces for TV
-page.tags="input","screens"
-
-trainingnavtop=true
-startpage=true
-
-@jd:body
-
-
-<p>
- Building an effective and engaging for TV devices requires a firm understanding what works well
- in the context of a living room. Imagine a large screen that can be seen by many people at the
- same time, controlled a few buttons by users with limited attention and you start to see the
- challenges and opportunity of building an app for TV. Building apps for this environment
- requires a different approach and different tools.</p>
-
-<p>This section discusses how to build a living room experience with your app, including
- implementation instructions and user interface widgets built for TV. Also check out the
- <a href="{@docRoot}design/tv/index.html">Design for TV</a> for information and inspiration
- on creating engaging user interfaces for TV devices.</p>
-
-<h2>Topics</h2>
-
-<dl>
- <dt><b><a href="layouts.html">Layouts</a></b></dt>
- <dd>Learn how to build app layouts for TV screens.</dd>
-
- <dt><b><a href="navigation.html">Navigation</a></b></dt>
- <dd>Learn how to build navigation for TV devices.</dd>
-
- <dt><b><a href="browse.html">BrowseFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a browsing interface for media catalogs.</dd>
-
- <dt><b><a href="details.html">DetailsFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a details page for media items.</dd>
-
- <dt><b><a href="search.html">In-App Search</a></b></dt>
- <dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
-
- <dt><b><a href="recommendations.html">Recommendations</a></b></dt>
- <dd>Learn how to contribute watch next suggestions and get your content noticed by users.</dd>
-</dl>
-
-
diff --git a/docs/html/preview/tv/ui/layouts.jd b/docs/html/preview/tv/ui/layouts.jd
deleted file mode 100644
index 5655152..0000000
--- a/docs/html/preview/tv/ui/layouts.jd
+++ /dev/null
@@ -1,298 +0,0 @@
-page.title=Layouts for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=Navigation for TV
-next.link=navigation.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#themes">Themes</a>
- <ol>
- <li><a href="#leanback-theme">Leanback Theme</a></li>
- </ol>
- </li>
- <li><a href="#structure">Layout Structure</a>
- <ol>
- <li><a href="#overscan">Overscan</a></li>
- </ol>
- </li>
- <li><a href="#visibility">Text and Controls Visibility</a></li>
- <li><a href="#density-resources">Screen Density and Image Resources</a></li>
- <li><a href="#anti-patterns">Layout Anti-Patterns</a></li>
- <li><a href="#large-bitmaps">Handling Large Bitmaps</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>
- A TV screen is typically viewed from about 10 feet away, and while it is much larger than most
- other Android device displays, this type of screen does not provide the same level of precise
- detail and color as a smaller device. These factors require that you create app layouts with
- TV devices in mind in order to create a useful and enjoyable user experience.</p>
-
-<p>This guide provides guidance and implementation details for building effective layouts for
- TV apps.</p>
-
-
-<h2 id="themes">Themes</h2>
-
-<p>Android <a href="{@docRoot}guide/topics/ui/themes.html">Themes</a> can provide a basis for
- layouts in your apps. On TV devices, you should use a theme to suppress the title bar from
- your app activities that are meant for TV. The title bar is a standard user interface element
- for Android apps on phones and tablets, but it is not appropriate for TV apps. The following
- code example from TV app manifest demonstrates how to apply this theme:
-</p>
-
-<pre>
-<application>
- ...
-
- <activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>>
- ...
-
- </activity>
-</application>
-
-</pre>
-
-
-<h3 id="leanback-theme">Leanback Theme</h3>
-
-<p>The Android framework provides a standard theme for TV activities, called {@code
- Leanback.Theme}, which establishes a consistent visual style for TV apps. Use of this theme is
- recommended for most apps. The following code sample shows how to apply this theme to a given
- activity within an app:</p>
-
-<pre>
-<activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.Leanback"</strong>>
-</pre>
-
-
-<h2 id="structure">Layout Structure</h2>
-
-<p>Layouts for TV devices should follow some basic guidelines to ensure they are useable and
- effective on large screens. Follow these tips to build landscape layouts optimized for TV screens:
-</p>
-
-<ul>
- <li>Build layouts with a landscape orientation. TV screens are always displayed in this
- orientation.</li>
- <li>Put on-screen navigation controls on the left or right side of the screen and save the
- vertical space for content.</li>
- <li>Create UIs that are divided into sections, using <a
- href="{@docRoot}guide/components/fragments.html"
- >Fragments</a> and use view groups like {@link android.widget.GridView} instead of {@link
- android.widget.ListView} to make better use of the horizontal screen space.
- </li>
- <li>Use view groups such as {@link android.widget.RelativeLayout} or {@link
- android.widget.LinearLayout} to arrange views. This approach allows the system to adjust the
- position of the views to the size, alignment, aspect ratio, and pixel density of a TV screen.</li>
- <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
-</ul>
-
-
-<h3 id="overscan">Overscan</h3>
-
-<p>Layouts for TV have some unique requirements due to the evolution of TV standards and the
- desire to always present a full screen picture to viewers. For this reason, TV devices may
- clip the outside edge of an app layout in order to ensure a that the entire display is filled.
- This behavior is generally referred to as Overscan.</p>
-
-<p>In order to account for the impact of overscan and make sure that all the user interface
- elements you place in a layout are actually shown on screen, you should incorporate a 10% margin
- on all sides of your layout. This translates into a 27dp margin on the left and right edges and
- a 48dp margin on the top and bottom of your base layouts for activities. The following
- example layout demonstrates how to set these margins in the root layout for a TV app:
-</p>
-
-<pre>
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/base_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_marginTop="27dp"
- android:layout_marginLeft="48dp"
- android:layout_marginRight="48dp"
- android:layout_marginBottom="27dp" >
-</LinearLayout>
-</pre>
-
-<p class="caution">
- <strong>Caution:</strong> Do not apply overscan margins to your layout if you are using the
- Leanback Support Library {@code BrowseFragment} or related widgets, as those layouts already
- incorporate overscan-safe margins.
-</p>
-
-
-<h2 id="visibility">Text and Controls Visibility</h2>
-
-<p>
-The text and controls in a TV app layout should be easily visible and navigable from a distance.
-Follow these tips to make them easier to see from a distance :
-</p>
-
-<ul>
- <li>Break text into small chunks that users can quickly scan.</li>
- <li>Use light text on a dark background. This style is easier to read on a TV.</li>
- <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes.
- Use simple sans-serif fonts and anti-aliasing to increase readability.</li>
- <li>Use Android's standard font sizes:
-<pre>
-<TextView
- android:id="@+id/atext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-</pre>
- </li>
- <li>Ensure that all your view widgets are large enough to be clearly visible to someone
- sitting 10 feet away from the screen (this distance is greater for very large screens). The
- best way to do this is to use layout-relative sizing rather than absolute sizing, and
- density-independent pixel units instead of absolute pixel units. For example, to set the
- width of a widget, use wrap_content instead of a pixel measurement, and to set the margin
- for a widget, use dip instead of px values.</li>
-</ul>
-
-
-<h2 id="density-resources">Screen Density and Image Resources</h2>
-
-<p>The common high-definition TV display resolutions are 720p, 1080i, and 1080p.
- Your TV layout should target a screen size of 1920 x 1080 pixels, and then allow the Android
- system to downscale your layout elements to 720p if necessary. In general, downscaling
- (removing pixels) does not degrade your layout presentation quality. However, upscaling can
- cause display artifacts that degrade the quality of your layout and have a negative impact on
- the user experience of your app.</p>
-
-<p>
- To get the best scaling results for images, provide them as
- <a href="{@docRoot}tools/help/draw9patch.html">9-patch image</a> elements if possible. If you
- provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or
- grainy. This is not a good experience for the user. Instead, use high-quality images.
-</p>
-
-<p>
- For more information on optimizing layouts and resources for large screens see
- <a href="{@docRoot}training/multiscreen/index.html">Designing for multiple screens</a>.
-</p>
-
-
-<h2 id="anti-patterns">Layout Anti-Patterns</h2>
-
-<p>There are a few approaches to building layouts for TV that you should avoid because they do not
-work well and lead to bad user experiences. Here are some user interface approaches you
-should specifically <em>not</em> use when developing a layout for TV.
-</p>
-
-<ul>
- <li><strong>Re-using phone or tablet layouts</strong> - Do not reuse layouts from a phone or
- tablet app without modification. Layouts built for other Android device form factors are not
- well suited for TV devices and should be simplified for operation on a TV.</li>
- <li><strong>ActionBar</strong> - While this user interface convention is recommended for use
- on phones and tablets, it is not appropriate for a TV interface. In particular, using an
- antion bar options menu (or any pull-down menu for that matter) is strongly discouraged, due
- to the difficulty in navigating such a menu with a remote control.</li>
- <li><strong>ViewPager</strong> - Sliding between screens can work great on a phone or tablet,
- but don't try this on a TV!</li>
-
-</ul>
-
-<p>For more information on designing layouts that are appropriate to TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.</p>
-
-
-<h2 id="large-bitmaps">Handling Large Bitmaps</h2>
-
-<p>TV devices, like any other Android device, have a limited amount of memory. If you build your
- app layout with very high-resolution images or use many high-resolutions images in the operation
- of your app, it can quickly run into memory limits and cause out of memory errors.
- To avoid these types of problems, follow these tips:</p>
-
-<ul>
- <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in
- a {@link android.widget.GridView} or
- {@link android.widget.Gallery}, only load an image when
- {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()}
- is called on the View's {@link android.widget.Adapter}.
- </li>
- <li>Call {@link android.graphics.Bitmap#recycle()} on
- {@link android.graphics.Bitmap} views that are no longer needed.
- </li>
- <li>Use {@link java.lang.ref.WeakReference} for storing references
- to {@link android.graphics.Bitmap} objects in an in-memory
- {@link java.util.Collection}.</li>
- <li>If you fetch images from the network, use {@link android.os.AsyncTask}
- to fetch them and store them on the SD card for faster access.
- Never do network transactions on the application's UI thread.
- </li>
- <li>Scale down large images to a more appropriate size as you download them;
- otherwise, downloading the image itself may cause an out of memory exception.
- The following sample code demonstrates how to scale down images while downloading:
-<pre>
- // Get the source image's dimensions
- BitmapFactory.Options options = new BitmapFactory.Options();
- // This does not download the actual image, just downloads headers.
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
- // The actual width of the image.
- int srcWidth = options.outWidth;
- // The actual height of the image.
- int srcHeight = options.outHeight;
-
- // Only scale if the source is bigger than the width of the destination view.
- if(desiredWidth > srcWidth)
- desiredWidth = srcWidth;
-
- // Calculate the correct inSampleSize/scale value. This approach helps reduce
- // memory use. This value should be a power of 2.
- int inSampleSize = 1;
- while(srcWidth / 2 > desiredWidth){
- srcWidth /= 2;
- srcHeight /= 2;
- inSampleSize *= 2;
- }
-
- float desiredScale = (float) desiredWidth / srcWidth;
-
- // Decode with inSampleSize
- options.inJustDecodeBounds = false;
- options.inDither = false;
- options.inSampleSize = inSampleSize;
- options.inScaled = false;
- // Ensures the image stays as a 32-bit ARGB_8888 image.
- // This preserves image quality.
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-
- Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
-
- // Resize
- Matrix matrix = new Matrix();
- matrix.postScale(desiredScale, desiredScale);
- Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
- sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
- sampledSrcBitmap = null;
-
- // Save
- FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
- scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
- scaledBitmap = null;
-</pre>
- </li>
-</ul>
-
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
deleted file mode 100644
index 3041e58..0000000
--- a/docs/html/preview/tv/ui/navigation.jd
+++ /dev/null
@@ -1,144 +0,0 @@
-page.title=Navigation for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Layouts for TV
-previous.link=layouts.html
-next.title=BrowseFragment
-next.link=browse.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#d-pad-navigation">D-pad Navigation</a></li>
- <li><a href="#focus-selection">Focus and Selection</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>TV devices provide a limited set of navigation controls for apps. Creating an effective
- navigation scheme for your TV app depends on understanding these limited controls and the limits
- of users perception while operating your app. As you build your Android application for TVs,
- you should pay special attention to how the user actually navigates around your application
- when using remote control buttons instead of a touch screen.</p>
-
-<p>This guide shows you how to build an effective navigation scheme for your TV app.</p>
-
-
-<h2 id="d-pad-navigation">D-pad Navigation</h2>
-
-<p>On a TV device, users navigate with controls on a remote control device, using either a
- directional pad (D-pad) or arrow keys. This type of control limits movement to up, down, left,
- and right. To build a great TV-optimized app, you must provide a navigation scheme where
- the user can quickly learn how to navigate your app using these limited controls.</p>
-
-<p>Follow these guidelines to build navigation system that works well with a D-pad on a TV device:
-</p>
-
-<ul>
- <li>Ensure that the D-pad can navigate to all the visible controls on the screen.</li>
- <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects
- an item in the list. Ensure that users can select an element in the list and that the list still
- scrolls when an element is selected.</li>
- <li>Ensure that movement between controls is straightforward and predictable.</li>
-</ul>
-
-<p>The Android framework handles directional navigation between layout elements automatically, so
- you typically do not need to do anything extra for your app. However, you should thoroughly test
- navigation with a D-pad control to discover any navigation problems. If you discover that your
- screen layout makes navigation difficult, or if you want users to move through the layout in a
- specific way, you can set up explicit directional navigation for your controls. The following
- code sample shows how to define the next control to receive focus for a
- {@link android.widget.TextView} layout object:</p>
-
-<pre>
-<TextView android:id="@+id/Category1"
- android:nextFocusDown="@+id/Category2"\>
-</pre>
-
-<p>The following table lists all of the available navigation attributes for Android user interface
-widgets:</p>
-
-<table>
- <tr>
- <th>Attribute</th>
- <th>Function</th>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusDown}</td>
- <td>Defines the next view to receive focus when the user navigates down.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusLeft}</td>
- <td>Defines the next view to receive focus when the user navigates left.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusRight}</td>
- <td>Defines the next view to receive focus when the user navigates right.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusUp}</td>
- <td>Defines the next view to receive focus when the user navigates up.</td>
- </tr>
-</table>
-
-<p>To use one of these explicit navigation attributes, set the value to the ID ({@code android:id}
- value) of another widget in the layout. You should set up the navigation order as a loop, so that
- the last control directs focus back to the first one.</p>
-
-<p class="note">
- <strong>Note:</strong> You should only use these attributes to modify the navigation order if the
- default order that the system applies does not work well.
-</p>
-
-
-<h2 id="focus-selection">Focus and Selection</h2>
-
-<p>The success of a navigation scheme on TV devices is strongly dependent on how easy it is for a
- user to determine what user interface element is in focus on screen. If you do not provide clear
- indications of what is in focus on screen (and therefore what item they can take action on),
- users can quickly become frustrated and exit your app. By the same token, it is important
- to always have an item in focus that a user can take action on immediately after your app starts,
- and any time your app is not playing content.</p>
-
-<p>Your app layout and implementation should use color, size, animation or a combination of
- these attributes to help users easily determine what actions they can take next. Use a uniform
- scheme for indicating focus across your application.</p>
-
-<p>Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">
-Drawable State List Resources</a> to implement highlights for selected and focused controls. The
-following code example demonstates how to apply selection indication for a button object:
-</p>
-
-<pre>
-<!-- res/drawable/button.xml -->
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/button_pressed" /> <!-- pressed -->
- <item android:state_focused="true"
- android:drawable="@drawable/button_focused" /> <!-- focused -->
- <item android:state_hovered="true"
- android:drawable="@drawable/button_focused" /> <!-- hovered -->
- <item android:drawable="@drawable/button_normal" /> <!-- default -->
-</selector>
-</pre>
-
-<p>
-This layout XML applies the above state list drawable to a {@link android.widget.Button}:
-</p>
-<pre>
-<Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:background="@drawable/button" />
-</pre>
-
-<p>Make sure to provide sufficient padding within the focusable and selectable controls so that
- the highlights around them are clearly visible.</p>
-
diff --git a/docs/html/preview/tv/ui/recommendations.jd b/docs/html/preview/tv/ui/recommendations.jd
deleted file mode 100644
index 52ac2e5..0000000
--- a/docs/html/preview/tv/ui/recommendations.jd
+++ /dev/null
@@ -1,234 +0,0 @@
-page.title=Making Recommendations
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Searching in TV Apps
-previous.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#service">Create a Recommendations Service</a></li>
- <li><a href="#build">Build Recommendations</a></li>
- <li><a href="#run-service">Run Recommendations Service</a></li>
- <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Content recommendations appear as the first row of the TV launch screen after the first use
- of the device. This row is intended to help users quickly find content they enjoy. Contributing
- recommendations from your apps content catalog can help bring users back to your app.</p>
-
-
-<img src="{@docRoot}preview/tv/images/home-recommendations.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> The first row after the search widget is the system-wide
- recommendations.
-</p>
-
-
-<h2 id="service">Create a Recommendations Service</h2>
-
-<p>Content recommendations are created with background processing. In order for your application
- to contribute to recommendations, you create a service that periodically adds listings from your
- app's catalog to the system list of recommendations.</p>
-
-<p>The following code example illustrates how to extend the {@link android.app.IntentService} to
- create a recommendation service for your application.</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-<p>In order for this class to be recognized and run as a service, you must register this service
- using your app manifest. The following code snippet illustrates how to add this class as a
- service:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- ...
-
- <service android:name=".UpdateRecommendationsService"
- android:enabled="true" android:exported="true"/>
- </application>
-</manifest>
-</pre>
-
-<h2 id="build">Build Recommendations</h2>
-
-<p>Once it starts running, your service must create recommendations and pass them to the Android
- framework. The framework receives the recommendations as {@link android.app.Notification} objects
- that use a specific style and are marked with a specific category.</p>
-
-<p>The following code example demonstrates how to get an instance of the {@link
- android.app.NotificationManager}, build a recommendation and post it to the manager:</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-
-<h3 id="run-service">Run Recommendations Service</h3>
-
-<p>Your app's recommendation service must run periodically in order to create current
- recommendations. In order to run your service, you should create a class that runs a timer and
- invokes it at regular intervals. The following code example extends the {@link
- android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
- every 30 minutes:</p>
-
-<pre>
-public class BootupActivity extends BroadcastReceiver {
- private static final String TAG = "BootupActivity";
-
- private static final long INITIAL_DELAY = 5000;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
- scheduleRecommendationUpdate(context);
- }
- }
-
- private void scheduleRecommendationUpdate(Context context) {
- AlarmManager alarmManager = (AlarmManager)context.getSystemService(
- Context.ALARM_SERVICE);
- Intent recommendationIntent = new Intent(context,
- UpdateRecommendationsService.class);
- PendingIntent alarmIntent = PendingIntent.getService(context, 0,
- recommendationIntent, 0);
-
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- INITIAL_DELAY,
- AlarmManager.INTERVAL_HALF_HOUR,
- alarmIntent);
- }
-}
-</pre>
-
-<p>In order for the {@link android.content.BroadcastReceiver} class to execute after an TV
- device starts up, you must register this class in your app manifest and attach an intent filter
- for the completion of the device boot process. This sample code demonstrates how to add this
- configuration to the manifest:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- <receiver android:name=".BootupActivity" android:enabled="true"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- </intent-filter>
- </receiver>
- </application>
-</manifest>
-</pre>
diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd
index c38c927..f6435f7 100644
--- a/docs/html/sdk/installing/adding-packages.jd
+++ b/docs/html/sdk/installing/adding-packages.jd
@@ -14,7 +14,7 @@
background:#eee;
}
ol.large > li:nth-child(odd) {
-}
+}
ol.large > li:before {
display:inline;
left:-40px;
@@ -76,7 +76,7 @@
<ol class="large">
<li>
- <h2 class="norule">Get the latest SDK tools</h2>
+ <h2 id="GetTools" class="norule">Get the latest SDK tools</h2>
<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" />
@@ -102,7 +102,7 @@
</li>
<li>
- <h2 class="norule">Get the support library for additional APIs</h2>
+ <h2 id="GetSupportLib" class="norule">Get the support library for additional APIs</h2>
<div class="sidebox">
<h3>Why use the support library?</h3>
@@ -145,7 +145,7 @@
<li>
- <h2 class="norule">Get Google Play services for even more APIs</h2>
+ <h2 id="GetGoogle" class="norule">Get Google Play services for even more APIs</h2>
<div class="sidebox">
<h3>Why use Google Play services?</h3>
@@ -182,7 +182,7 @@
<li>
- <h2 class="norule">Build something!</h2>
+ <h2 id="Build" class="norule">Build something!</h2>
<p>With the above packages now in your Android SDK, you're ready to build apps
for Android. As new tools and other APIs become available, simply launch the SDK Manager
@@ -208,8 +208,8 @@
<h3>Use Google APIs</h3>
<p>To start using Google APIs, such as Maps or
Play Game services, see the guide to
-<strong><a href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services
-APIs</a></strong>.</p>
+<strong><a href="{@docRoot}google/play-services/setup.html">Setting Up Google Play
+Services</a></strong>.</p>
</div>
</div><!-- end cols -->
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index af6bd75..c97afa5 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -619,7 +619,7 @@
function onDownloadForRealz(link) {
if ($("input#agree").is(':checked')) {
- $("h1").text('Now downloading Android Studio...');
+ $("h1").text('Now redirecting to the install instructions...');
$("#tos").slideUp();
$("#jd-content .jd-descr").fadeOut('slow', function() {
setTimeout(function() {
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 026698c..59d5506 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -51,7 +51,7 @@
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
</div>
@@ -62,7 +62,7 @@
</div> <!-- end .wrap -->
<div class="landing-scroll-down-affordance">
<a class="landing-down-arrow" href="#extending-android-to-wearables">
- <img src="/wear/images/carrot.png" alt="Scroll down to read more">
+ <img src="{@docRoot}wear/images/carrot.png" alt="Scroll down to read more">
</a>
</div>
</div> <!-- end .landing-section .landing-hero -->
@@ -85,8 +85,8 @@
<div class="col-3-wide">
<div class="landing-inset-video-container">
- <img class="landing-bezel-only" src="/wear/images/screens/bezel.png" alt="">
- <img class="gif" src="/wear/images/screens/reservation_animated.gif">
+ <img class="landing-bezel-only" src="{@docRoot}wear/images/screens/bezel.png" alt="">
+ <img class="gif" src="{@docRoot}wear/images/screens/reservation_animated.gif">
</div>
<p class="landing-small">
@@ -94,7 +94,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/circle_message2.png" itemprop="image" alt="">
+ <img src="{@docRoot}wear/images/screens/circle_message2.png" itemprop="image" alt="">
<p class="landing-small">
Get glanceable, actionable information at just the right time with notifications
that are synced from your handheld device.
@@ -102,7 +102,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/fitness-24.png" alt="">
+ <img src="{@docRoot}wear/images/screens/fitness-24.png" alt="">
<p class="landing-small">
Design apps that can access a wide range of sensors and other hardware
directly on the wearable.
@@ -113,7 +113,7 @@
<p>
Before you start building, check out the
- <a href="/design/devices/wear.html">Android Wear Design Principles</a>
+ <a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a>
to understand how to create great experiences for this exciting, new form factor.</p>
</div>
@@ -133,49 +133,49 @@
<div class="landing-body">
<div class="landing-breakout cols">
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Synced Notifications</p>
<p class="landing-small">
Notifications on handhelds can automatically sync to wearables, so design them
with both devices in mind.
</p>
<p class="landing-small">
- <a href="/training/wearables/notifications/index.html">Build notifications</a>
+ <a href="{@docRoot}training/wearables/notifications/index.html">Build notifications</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts1.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts1.png" alt="">
<p>Wearable Apps</p>
<p class="landing-small">
Create custom experiences with activities, services, sensors, and much
more with the Android SDK.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Create wearable apps</a>
+ <a href="{@docRoot}training/wearables/apps/index.html">Create wearable apps</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Send Data</p>
<p class="landing-small">
Send data and actions between handhelds and wearables with
data replication APIs and RPCs.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Work with the Data Layer</a>
+ <a href="{@docRoot}training/wearables/data-layer/index.html">Work with the Data Layer</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts4.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts4.png" alt="">
<p>Voice Actions</p>
<p class="landing-small">
Register your app to handle voice actions, like "Ok Google, take a note,"
for a hands-free experience.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Integrate voice actions</a>
+ <a href="{@docRoot}training/wearables/apps/voice-actions.html">Integrate voice actions</a>
</p>
</div>
</div>
@@ -183,58 +183,6 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
-<!--
-
- <div class="landing-section landing-white-background">
- <div class="wrap">
- <div class="landing-section-header">
- <div class="landing-h2">Building an Ecosystem</div>
- <div class="landing-body landing-align-center">
- <p class="landing-small">
- We’re working with several partners to bring you watches powered by Android Wear later this year!
- </p>
- </div>
- </div>
-
- <div class="landing-partners cols">
- <div class="col-4">
- <img src="/wear/images/partners/asus.png" alt="Asus">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/broadcom.png" alt="Broadcom">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/fossil.png" alt="Fossil">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/htc.png" alt="HTC">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/intel.png" alt="Intel">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/lg.png" alt="LG">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mediatek.png" alt="Mediatek">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mips.png" alt="MIPS">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/motorola.png" alt="Motorola">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/qualcomm.png" alt="Qualcomm">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/samsung.png" alt="Samsung">
- </div>
- </div>
- </div> <!-- end .wrap
-
- </div> <!-- end .landing-section -->
-
<div class="landing-section landing-red-background">
<div class="wrap">
<div class="landing-section-header">
@@ -248,7 +196,7 @@
</div>
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-secondary" style="margin-top: 20px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-secondary" style="margin-top: 20px;">
Get Started
</a>
</div>
@@ -277,7 +225,7 @@
</div>
<div class="col-3-wide">
<a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-landing-developer-preview.html">
- <img class="landing-social-image" src="/wear/images/blogger.png" alt="">
+ <img class="landing-social-image" src="{@docRoot}wear/images/blogger.png" alt="">
</a>
<div class="landing-social-copy">
<p>Blog Post</p>
@@ -313,8 +261,6 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
</div> <!-- end .landing-rest-of-page -->
-
-
<div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
<div class="layout-content-col col-16" style="padding-top:4px">
<style>#___plusone_0 {float:right !important;}</style>
@@ -326,7 +272,7 @@
Except as noted, this content is
licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
Creative Commons Attribution 2.5</a>. For details and
- restrictions, see the <a href="/license.html">Content
+ restrictions, see the <a href="{@docRoot}license.html">Content
License</a>.
</div>
</div>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 13789ca..2b36016 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -65,11 +65,11 @@
/**
* Used to determine when compatibility scaling is in effect.
- *
+ *
* @hide
*/
protected int mScreenDensity = Bitmap.DENSITY_NONE;
-
+
// Used by native code
@SuppressWarnings("UnusedDeclaration")
private int mSurfaceFormat;
@@ -79,7 +79,7 @@
* @hide
*/
public static final int DIRECTION_LTR = 0;
-
+
/**
* Flag for drawTextRun indicating right-to-left run direction.
* @hide
@@ -136,7 +136,7 @@
/**
* Construct a canvas with the specified bitmap to draw into. The bitmap
* must be mutable.
- *
+ *
* <p>The initial target density of the canvas is the same as the given
* bitmap's density.
*
@@ -177,10 +177,10 @@
/**
* Indicates whether this Canvas uses hardware acceleration.
- *
+ *
* Note that this method does not define what type of hardware acceleration
* may or may not be used.
- *
+ *
* @return True if drawing operations are hardware accelerated,
* false otherwise.
*/
@@ -189,7 +189,7 @@
}
/**
- * Specify a bitmap for the canvas to draw into. All canvas state such as
+ * Specify a bitmap for the canvas to draw into. All canvas state such as
* layers, filters, and the save/restore stack are reset with the exception
* of the current matrix and clip stack. Additionally, as a side-effect
* the canvas' target density is updated to match that of the bitmap.
@@ -279,7 +279,7 @@
* to determine the scaling factor when drawing a bitmap into it.
*
* @see #setDensity(int)
- * @see Bitmap#getDensity()
+ * @see Bitmap#getDensity()
*/
public int getDensity() {
return mDensity;
@@ -295,7 +295,7 @@
* {@link Bitmap#DENSITY_NONE} to disable bitmap scaling.
*
* @see #getDensity()
- * @see Bitmap#setDensity(int)
+ * @see Bitmap#setDensity(int)
*/
public void setDensity(int density) {
if (mBitmap != null) {
@@ -313,19 +313,19 @@
* Returns the maximum allowed width for bitmaps drawn with this canvas.
* Attempting to draw with a bitmap wider than this value will result
* in an error.
- *
- * @see #getMaximumBitmapHeight()
+ *
+ * @see #getMaximumBitmapHeight()
*/
public int getMaximumBitmapWidth() {
return MAXMIMUM_BITMAP_SIZE;
}
-
+
/**
* Returns the maximum allowed height for bitmaps drawn with this canvas.
* Attempting to draw with a bitmap taller than this value will result
* in an error.
- *
- * @see #getMaximumBitmapWidth()
+ *
+ * @see #getMaximumBitmapWidth()
*/
public int getMaximumBitmapHeight() {
return MAXMIMUM_BITMAP_SIZE;
@@ -357,8 +357,8 @@
/** clip against the layer's bounds */
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
/** restore everything when restore() is called */
- public static final int ALL_SAVE_FLAG = 0x1F;
-
+ public static final int ALL_SAVE_FLAG = 0x1F;
+
/**
* Saves the current matrix and clip onto a private stack. Subsequent
* calls to translate,scale,rotate,skew,concat or clipRect,clipPath
@@ -371,7 +371,7 @@
public int save() {
return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
}
-
+
/**
* Based on saveFlags, can save the current matrix and clip onto a private
* stack. Subsequent calls to translate,scale,rotate,skew,concat or
@@ -589,25 +589,25 @@
public void concat(@Nullable Matrix matrix) {
if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance);
}
-
+
/**
* Completely replace the current matrix with the specified matrix. If the
* matrix parameter is null, then the current matrix is reset to identity.
- *
+ *
* <strong>Note:</strong> it is recommended to use {@link #concat(Matrix)},
* {@link #scale(float, float)}, {@link #translate(float, float)} and
* {@link #rotate(float)} instead of this method.
*
* @param matrix The matrix to replace the current matrix with. If it is
* null, set the current matrix to identity.
- *
- * @see #concat(Matrix)
+ *
+ * @see #concat(Matrix)
*/
public void setMatrix(@Nullable Matrix matrix) {
native_setMatrix(mNativeCanvasWrapper,
matrix == null ? 0 : matrix.native_instance);
}
-
+
/**
* Return, in ctm, the current transformation matrix. This does not alter
* the matrix in the canvas, but just returns a copy of it.
@@ -628,7 +628,7 @@
getMatrix(m);
return m;
}
-
+
/**
* Modify the current clip with the specified rectangle.
*
@@ -677,7 +677,7 @@
return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
Region.Op.INTERSECT.nativeInt);
}
-
+
/**
* Modify the current clip with the specified rectangle, which is
* expressed in local coordinates.
@@ -744,7 +744,7 @@
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
}
-
+
/**
* Intersect the current clip with the specified path.
*
@@ -754,7 +754,7 @@
public boolean clipPath(@NonNull Path path) {
return clipPath(path, Region.Op.INTERSECT);
}
-
+
/**
* Modify the current clip with the specified region. Note that unlike
* clipRect() and clipPath() which transform their arguments by the
@@ -789,11 +789,11 @@
public boolean clipRegion(@NonNull Region region) {
return clipRegion(region, Region.Op.INTERSECT);
}
-
+
public @Nullable DrawFilter getDrawFilter() {
return mDrawFilter;
}
-
+
public void setDrawFilter(@Nullable DrawFilter filter) {
long nativeFilter = 0;
if (filter != null) {
@@ -814,7 +814,7 @@
* Antialiased: Treat edges by rounding-out, since they may be antialiased
*/
AA(1);
-
+
EdgeType(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -900,7 +900,7 @@
public boolean getClipBounds(@Nullable Rect bounds) {
return native_getClipBounds(mNativeCanvasWrapper, bounds);
}
-
+
/**
* Retrieve the bounds of the current clip (in local coordinates).
*
@@ -911,7 +911,7 @@
getClipBounds(r);
return r;
}
-
+
/**
* Fill the entire canvas' bitmap (restricted to the current clip) with the
* specified RGB color, using srcover porterduff mode.
@@ -968,7 +968,7 @@
public void drawPaint(@NonNull Paint paint) {
native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint);
}
-
+
/**
* Draw a series of points. Each point is centered at the coordinate
* specified by pts[], and its diameter is specified by the paint's stroke
@@ -1065,7 +1065,7 @@
public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
-
+
/**
* Draw the specified Rect using the specified paint. The rectangle will
@@ -1111,15 +1111,15 @@
/**
* <p>Draw the specified arc, which will be scaled to fit inside the
* specified oval.</p>
- *
+ *
* <p>If the start angle is negative or >= 360, the start angle is treated
* as start angle modulo 360.</p>
- *
+ *
* <p>If the sweep angle is >= 360, then the oval is drawn
* completely. Note that this differs slightly from SkPath::arcTo, which
* treats the sweep angle modulo 360. If the sweep angle is negative,
* the sweep angle is treated as sweep angle modulo 360</p>
- *
+ *
* <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
* geometric angle of 0 degrees (3 o'clock on a watch.)</p>
*
@@ -1197,7 +1197,7 @@
* @param patch The ninepatch object to render
* @param dst The destination rectangle.
* @param paint The paint to draw the bitmap with. may be null
- *
+ *
* @hide
*/
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
@@ -1220,7 +1220,7 @@
/**
* Draw the specified bitmap, with its top/left corner at (x,y), using
* the specified paint, transformed by the current matrix.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -1230,7 +1230,7 @@
* <p>If the bitmap and canvas have different densities, this function
* will take care of automatically scaling the bitmap to draw at the
* same density as the canvas.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param left The position of the left side of the bitmap being drawn
* @param top The position of the top side of the bitmap being drawn
@@ -1246,7 +1246,7 @@
* Draw the specified bitmap, scaling/translating automatically to fill
* the destination rectangle. If the source rectangle is not null, it
* specifies the subset of the bitmap to draw.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -1257,7 +1257,7 @@
* This is because the source and destination rectangle coordinate
* spaces are in their respective densities, so must already have the
* appropriate scaling factor applied.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param src May be null. The subset of the bitmap to be drawn
* @param dst The rectangle that the bitmap will be scaled/translated
@@ -1278,7 +1278,7 @@
* Draw the specified bitmap, scaling/translating automatically to fill
* the destination rectangle. If the source rectangle is not null, it
* specifies the subset of the bitmap to draw.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -1289,7 +1289,7 @@
* This is because the source and destination rectangle coordinate
* spaces are in their respective densities, so must already have the
* appropriate scaling factor applied.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param src May be null. The subset of the bitmap to be drawn
* @param dst The rectangle that the bitmap will be scaled/translated
@@ -1305,7 +1305,7 @@
native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
-
+
/**
* Treat the specified array of colors as a bitmap, and draw it. This gives
* the same result as first creating a bitmap from the array, and then
@@ -1394,7 +1394,7 @@
throw new ArrayIndexOutOfBoundsException();
}
}
-
+
/**
* Draw the bitmap through the mesh, where mesh vertices are evenly
* distributed across the bitmap. There are meshWidth+1 vertices across, and
@@ -1446,7 +1446,7 @@
TRIANGLES(0),
TRIANGLE_STRIP(1),
TRIANGLE_FAN(2);
-
+
VertexMode(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -1456,7 +1456,7 @@
*/
public final int nativeInt;
}
-
+
/**
* Draw the array of vertices, interpreted as triangles (based on mode). The
* verts array is required, and specifies the x,y pairs for each vertex. If
@@ -1485,7 +1485,7 @@
* @param indices If not null, array of indices to reference into the
* vertex (texs, colors) array.
* @param indexCount number of entries in the indices array (if not null).
- * @param paint Specifies the shader to use if the texs array is non-null.
+ * @param paint Specifies the shader to use if the texs array is non-null.
*/
public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
@@ -1595,7 +1595,7 @@
* bidi on the provided text, but renders it as a uniform right-to-left or
* left-to-right run, as indicated by dir. Alignment of the text is as
* determined by the Paint's TextAlign value.
- *
+ *
* @param text the text to render
* @param index the start of the text to render
* @param count the count of chars to render
@@ -1606,13 +1606,12 @@
* + count.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
- * @param dir the run direction, either {@link #DIRECTION_LTR} or
- * {@link #DIRECTION_RTL}.
+ * @param isRtl whether the run is in RTL direction
* @param paint the paint
* @hide
*/
public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex,
- int contextCount, float x, float y, int dir, @NonNull Paint paint) {
+ int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1623,12 +1622,9 @@
if ((index | count | text.length - index - count) < 0) {
throw new IndexOutOfBoundsException();
}
- if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown dir: " + dir);
- }
native_drawTextRun(mNativeCanvasWrapper, text, index, count,
- contextIndex, contextCount, x, y, dir, paint.mNativePaint, paint.mNativeTypeface);
+ contextIndex, contextCount, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface);
}
/**
@@ -1644,12 +1640,12 @@
* position can be used for shaping context.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
- * @param dir the run direction, either 0 for LTR or 1 for RTL.
+ * @param isRtl whether the run is in RTL direction
* @param paint the paint
* @hide
*/
public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
- int contextEnd, float x, float y, int dir, @NonNull Paint paint) {
+ int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1661,22 +1657,20 @@
throw new IndexOutOfBoundsException();
}
- int flags = dir == 0 ? 0 : 1;
-
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end,
- contextStart, contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ contextStart, contextEnd, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, flags, paint);
+ contextStart, contextEnd, x, y, isRtl, paint);
} else {
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
- 0, contextLen, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ 0, contextLen, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface);
TemporaryBuffer.recycle(buf);
}
}
@@ -1684,7 +1678,7 @@
/**
* Draw the text in the array, with each character's origin specified by
* the pos array.
- *
+ *
* This method does not support glyph composition and decomposition and
* should therefore not be used to render complex scripts.
*
@@ -1708,7 +1702,7 @@
/**
* Draw the text in the array, with each character's origin specified by
* the pos array.
- *
+ *
* This method does not support glyph composition and decomposition and
* should therefore not be used to render complex scripts.
*
@@ -1776,7 +1770,7 @@
* <p>
* <strong>Note:</strong> This forces the picture to internally call
* {@link Picture#endRecording} in order to prepare for playback.
- *
+ *
* @param picture The picture to be drawn
*/
public void drawPicture(@NonNull Picture picture) {
@@ -1785,7 +1779,7 @@
picture.draw(this);
restoreToCount(restoreCount);
}
-
+
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
@@ -1798,7 +1792,7 @@
drawPicture(picture);
restore();
}
-
+
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
@@ -1977,11 +1971,11 @@
private static native void native_drawTextRun(long nativeCanvas, String text,
int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, long nativePaint, long nativeTypeface);
+ float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
private static native void native_drawTextRun(long nativeCanvas, char[] text,
int start, int count, int contextStart, int contextCount,
- float x, float y, int flags, long nativePaint, long nativeTypeface);
+ float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
private static native void native_drawPosText(long nativeCanvas,
char[] text, int index,
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 4268a24..8837955 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -56,7 +56,7 @@
* @hide
*/
public int mBidiFlags = BIDI_DEFAULT_LTR;
-
+
static final Style[] sStyleArray = {
Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
};
@@ -202,14 +202,14 @@
/**
* Bidi flag to set LTR paragraph direction.
- *
+ *
* @hide
*/
public static final int BIDI_LTR = 0x0;
/**
* Bidi flag to set RTL paragraph direction.
- *
+ *
* @hide
*/
public static final int BIDI_RTL = 0x1;
@@ -217,7 +217,7 @@
/**
* Bidi flag to detect paragraph direction via heuristics, defaulting to
* LTR.
- *
+ *
* @hide
*/
public static final int BIDI_DEFAULT_LTR = 0x2;
@@ -225,21 +225,21 @@
/**
* Bidi flag to detect paragraph direction via heuristics, defaulting to
* RTL.
- *
+ *
* @hide
*/
public static final int BIDI_DEFAULT_RTL = 0x3;
/**
* Bidi flag to override direction to all LTR (ignore bidi).
- *
+ *
* @hide
*/
public static final int BIDI_FORCE_LTR = 0x4;
/**
* Bidi flag to override direction to all RTL (ignore bidi).
- *
+ *
* @hide
*/
public static final int BIDI_FORCE_RTL = 0x5;
@@ -331,7 +331,7 @@
* either FILL or STROKE.
*/
FILL_AND_STROKE (2);
-
+
Style(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -357,7 +357,7 @@
* of the path.
*/
SQUARE (2);
-
+
private Cap(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -381,7 +381,7 @@
* The outer edges of a join meet with a straight line
*/
BEVEL (2);
-
+
private Join(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -405,7 +405,7 @@
* The text is drawn to the left of the x,y origin
*/
RIGHT (2);
-
+
private Align(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -418,7 +418,7 @@
public Paint() {
this(0);
}
-
+
/**
* Create a new paint with the specified flags. Use setFlags() to change
* these after the paint is created.
@@ -475,7 +475,7 @@
setTextLocale(Locale.getDefault());
setElegantTextHeight(false);
}
-
+
/**
* Copy the fields from src into this paint. This is equivalent to calling
* get() on all of the src fields, and calling the corresponding set()
@@ -529,7 +529,7 @@
/**
* Return the bidi flags on the paint.
- *
+ *
* @return the bidi flags on the paint
* @hide
*/
@@ -552,7 +552,7 @@
/**
* Return the paint's flags. Use the Flag enum to test flag values.
- *
+ *
* @return the paint's flags (see enums ending in _Flag for bit masks)
*/
public native int getFlags();
@@ -587,7 +587,7 @@
public final boolean isAntiAlias() {
return (getFlags() & ANTI_ALIAS_FLAG) != 0;
}
-
+
/**
* Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
* AntiAliasing smooths out the edges of what is being drawn, but is has
@@ -597,7 +597,7 @@
* @param aa true to set the antialias bit in the flags, false to clear it
*/
public native void setAntiAlias(boolean aa);
-
+
/**
* Helper for getFlags(), returning true if DITHER_FLAG bit is set
* Dithering affects how colors that are higher precision than the device
@@ -611,7 +611,7 @@
public final boolean isDither() {
return (getFlags() & DITHER_FLAG) != 0;
}
-
+
/**
* Helper for setFlags(), setting or clearing the DITHER_FLAG bit
* Dithering affects how colors that are higher precision than the device
@@ -623,7 +623,7 @@
* @param dither true to set the dithering bit in flags, false to clear it
*/
public native void setDither(boolean dither);
-
+
/**
* Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
*
@@ -649,7 +649,7 @@
public final boolean isSubpixelText() {
return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0;
}
-
+
/**
* Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
*
@@ -657,7 +657,7 @@
* flags, false to clear it.
*/
public native void setSubpixelText(boolean subpixelText);
-
+
/**
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
*
@@ -708,7 +708,7 @@
* flags, false to clear it.
*/
public native void setFakeBoldText(boolean fakeBoldText);
-
+
/**
* Whether or not the bitmap filter is activated.
* Filtering affects the sampling of bitmaps when they are transformed.
@@ -720,13 +720,13 @@
public final boolean isFilterBitmap() {
return (getFlags() & FILTER_BITMAP_FLAG) != 0;
}
-
+
/**
* Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit.
* Filtering affects the sampling of bitmaps when they are transformed.
* Filtering does not affect how the colors in the bitmap are converted into
* device pixels. That is dependent on dithering and xfermodes.
- *
+ *
* @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
* flags, false to clear it.
*/
@@ -773,7 +773,7 @@
* @param color The new color (including alpha) to set in the paint.
*/
public native void setColor(int color);
-
+
/**
* Helper to getColor() that just returns the color's alpha value. This is
* the same as calling getColor() >>> 24. It always returns a value between
@@ -1285,7 +1285,7 @@
*/
public static class FontMetrics {
/**
- * The maximum distance above the baseline for the tallest glyph in
+ * The maximum distance above the baseline for the tallest glyph in
* the font at a given text size.
*/
public float top;
@@ -1298,7 +1298,7 @@
*/
public float descent;
/**
- * The maximum distance below the baseline for the lowest glyph in
+ * The maximum distance below the baseline for the lowest glyph in
* the font at a given text size.
*/
public float bottom;
@@ -1307,7 +1307,7 @@
*/
public float leading;
}
-
+
/**
* Return the font's recommended interline spacing, given the Paint's
* settings for typeface, textSize, etc. If metrics is not null, return the
@@ -1318,7 +1318,7 @@
* @return the font's recommended interline spacing.
*/
public native float getFontMetrics(FontMetrics metrics);
-
+
/**
* Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
* with it, returning the object.
@@ -1328,7 +1328,7 @@
getFontMetrics(fm);
return fm;
}
-
+
/**
* Convenience method for callers that want to have FontMetrics values as
* integers.
@@ -1339,7 +1339,7 @@
public int descent;
public int bottom;
public int leading;
-
+
@Override public String toString() {
return "FontMetricsInt: top=" + top + " ascent=" + ascent +
" descent=" + descent + " bottom=" + bottom +
@@ -1364,7 +1364,7 @@
getFontMetricsInt(fm);
return fm;
}
-
+
/**
* Return the recommend line spacing based on the current typeface and
* text size.
@@ -1407,7 +1407,7 @@
}
private native float native_measureText(char[] text, int index, int count, int bidiFlags);
-
+
/**
* Return the width of the text.
*
@@ -1439,7 +1439,7 @@
}
private native float native_measureText(String text, int start, int end, int bidiFlags);
-
+
/**
* Return the width of the text.
*
@@ -1466,7 +1466,7 @@
}
private native float native_measureText(String text, int bidiFlags);
-
+
/**
* Return the width of the text.
*
@@ -1503,7 +1503,7 @@
TemporaryBuffer.recycle(buf);
return result;
}
-
+
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
* Return the number of chars that were measured, and if measuredWidth is
@@ -1738,7 +1738,7 @@
if (end - start > widths.length) {
throw new ArrayIndexOutOfBoundsException();
}
-
+
if (text.length() == 0 || start == end) {
return 0;
}
@@ -1755,7 +1755,7 @@
}
return res;
}
-
+
/**
* Return the advance widths for the characters in the string.
*
@@ -1816,15 +1816,12 @@
* @hide
*/
public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, int flags, float[] advances,
+ int contextIndex, int contextCount, boolean isRtl, float[] advances,
int advancesIndex) {
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
- if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown flags value: " + flags);
- }
if ((index | count | contextIndex | contextCount | advancesIndex
| (index - contextIndex) | (contextCount - count)
| ((contextIndex + contextCount) - (index + count))
@@ -1839,13 +1836,13 @@
}
if (!mHasCompatScaling) {
return native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, flags, advances, advancesIndex);
+ contextIndex, contextCount, isRtl, advances, advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
float res = native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
- contextIndex, contextCount, flags, advances, advancesIndex);
+ contextIndex, contextCount, isRtl, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -1864,7 +1861,7 @@
* @hide
*/
public float getTextRunAdvances(CharSequence text, int start, int end,
- int contextStart, int contextEnd, int flags, float[] advances,
+ int contextStart, int contextEnd, boolean isRtl, float[] advances,
int advancesIndex) {
if (text == null) {
@@ -1880,16 +1877,16 @@
if (text instanceof String) {
return getTextRunAdvances((String) text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex);
+ contextStart, contextEnd, isRtl, advances, advancesIndex);
}
if (text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunAdvances(text.toString(), start, end,
- contextStart, contextEnd, flags, advances, advancesIndex);
+ contextStart, contextEnd, isRtl, advances, advancesIndex);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunAdvances(start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, this);
+ contextStart, contextEnd, isRtl, advances, advancesIndex, this);
}
if (text.length() == 0 || end == start) {
return 0f;
@@ -1900,7 +1897,7 @@
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
float result = getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, flags, advances, advancesIndex);
+ 0, contextLen, isRtl, advances, advancesIndex);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -1937,8 +1934,7 @@
* must be <= start
* @param contextEnd the index past the last character to use for shaping context,
* must be >= end
- * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
- * or {@link #DIRECTION_RTL}
+ * @param isRtl whether the run is in RTL direction
* @param advances array to receive the advances, must have room for all advances,
* can be null if only total advance is needed
* @param advancesIndex the position in advances at which to put the
@@ -1948,14 +1944,11 @@
* @hide
*/
public float getTextRunAdvances(String text, int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex) {
+ int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
- if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown flags value: " + flags);
- }
if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
| (start - contextStart) | (contextEnd - end)
| (text.length() - contextEnd)
@@ -1970,13 +1963,13 @@
if (!mHasCompatScaling) {
return native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex);
+ contextStart, contextEnd, isRtl, advances, advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
float totalAdvance = native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex);
+ contextStart, contextEnd, isRtl, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -2005,7 +1998,7 @@
* @param text the text
* @param contextStart the start of the context
* @param contextLength the length of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -2014,7 +2007,7 @@
* @hide
*/
public int getTextRunCursor(char[] text, int contextStart, int contextLength,
- int flags, int offset, int cursorOpt) {
+ int dir, int offset, int cursorOpt) {
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -2024,7 +2017,7 @@
}
return native_getTextRunCursor(mNativePaint, text,
- contextStart, contextLength, flags, offset, cursorOpt);
+ contextStart, contextLength, dir, offset, cursorOpt);
}
/**
@@ -2045,7 +2038,7 @@
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -2054,22 +2047,22 @@
* @hide
*/
public int getTextRunCursor(CharSequence text, int contextStart,
- int contextEnd, int flags, int offset, int cursorOpt) {
+ int contextEnd, int dir, int offset, int cursorOpt) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunCursor(text.toString(), contextStart, contextEnd,
- flags, offset, cursorOpt);
+ dir, offset, cursorOpt);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunCursor(
- contextStart, contextEnd, flags, offset, cursorOpt, this);
+ contextStart, contextEnd, dir, offset, cursorOpt, this);
}
int contextLen = contextEnd - contextStart;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- int result = getTextRunCursor(buf, 0, contextLen, flags, offset - contextStart, cursorOpt);
+ int result = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -2092,7 +2085,7 @@
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -2101,7 +2094,7 @@
* @hide
*/
public int getTextRunCursor(String text, int contextStart, int contextEnd,
- int flags, int offset, int cursorOpt) {
+ int dir, int offset, int cursorOpt) {
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
| (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2110,7 +2103,7 @@
}
return native_getTextRunCursor(mNativePaint, text,
- contextStart, contextEnd, flags, offset, cursorOpt);
+ contextStart, contextEnd, dir, offset, cursorOpt);
}
/**
@@ -2156,7 +2149,7 @@
native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
path.ni());
}
-
+
/**
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
@@ -2176,7 +2169,7 @@
}
nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
}
-
+
/**
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
@@ -2197,7 +2190,7 @@
nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
bounds);
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -2252,15 +2245,15 @@
private static native float native_getTextRunAdvances(long native_object, long native_typeface,
char[] text, int index, int count, int contextIndex, int contextCount,
- int flags, float[] advances, int advancesIndex);
+ boolean isRtl, float[] advances, int advancesIndex);
private static native float native_getTextRunAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex);
+ boolean isRtl, float[] advances, int advancesIndex);
private native int native_getTextRunCursor(long native_object, char[] text,
- int contextStart, int contextLength, int flags, int offset, int cursorOpt);
+ int contextStart, int contextLength, int dir, int offset, int cursorOpt);
private native int native_getTextRunCursor(long native_object, String text,
- int contextStart, int contextEnd, int flags, int offset, int cursorOpt);
+ int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
private static native void native_getTextPath(long native_object, long native_typeface,
int bidiFlags, char[] text, int index, int count, float x, float y, long path);
diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java
index 9dc62c3..4edfad4 100644
--- a/graphics/java/android/graphics/drawable/Animatable.java
+++ b/graphics/java/android/graphics/drawable/Animatable.java
@@ -32,7 +32,7 @@
/**
* Indicates whether the animation is running.
- *
+ *
* @return True if the animation is running, false otherwise.
*/
boolean isRunning();
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index e8024f7..0fd4423 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -19,6 +19,8 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.ColorFilter;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -88,6 +90,7 @@
canvas.restoreToCount(saveCount);
}
+ @Override
public void start() {
if (!mRunning) {
mRunning = true;
@@ -95,11 +98,13 @@
}
}
+ @Override
public void stop() {
mRunning = false;
unscheduleSelf(this);
}
+ @Override
public boolean isRunning() {
return mRunning;
}
@@ -108,10 +113,11 @@
unscheduleSelf(this);
scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
}
-
+
+ @Override
public void run() {
// TODO: This should be computed in draw(Canvas), based on the amount
- // of time since the last frame drawn
+ // of time since the last frame drawn
mCurrentDegrees += mIncrement;
if (mCurrentDegrees > (360.0f - mIncrement)) {
mCurrentDegrees = 0.0f;
@@ -119,7 +125,7 @@
invalidateSelf();
nextFrame();
}
-
+
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
@@ -133,8 +139,8 @@
unscheduleSelf(this);
}
return changed;
- }
-
+ }
+
/**
* Returns the drawable rotated by this RotateDrawable.
*/
@@ -148,7 +154,7 @@
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
-
+
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
@@ -165,10 +171,16 @@
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
+ @Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
@@ -176,6 +188,7 @@
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
@@ -183,6 +196,7 @@
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
@@ -194,7 +208,7 @@
public boolean getPadding(Rect padding) {
return mState.mDrawable.getPadding(padding);
}
-
+
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
@@ -206,6 +220,16 @@
}
@Override
+ protected boolean onLevelChange(int level) {
+ return mState.mDrawable.setLevel(level);
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ return mState.mDrawable.setState(state);
+ }
+
+ @Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
}
@@ -231,11 +255,11 @@
final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
-
+
TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
-
+
tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
@@ -250,7 +274,7 @@
}
a.recycle();
-
+
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
@@ -306,7 +330,7 @@
Drawable mDrawable;
int mChangingConfigurations;
-
+
boolean mPivotXRel;
float mPivotX;
boolean mPivotYRel;
@@ -315,7 +339,7 @@
int mFramesCount;
private boolean mCanConstantState;
- private boolean mCheckedConstantState;
+ private boolean mCheckedConstantState;
public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner,
Resources res) {
@@ -341,12 +365,12 @@
public Drawable newDrawable() {
return new AnimatedRotateDrawable(this, null);
}
-
+
@Override
public Drawable newDrawable(Resources res) {
return new AnimatedRotateDrawable(this, res);
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 1ee0224..0740761 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -28,7 +28,6 @@
import android.util.AttributeSet;
/**
- *
* An object used to create frame-by-frame animations, defined by a series of Drawable objects,
* which can be used as a View object's background.
* <p>
@@ -114,6 +113,7 @@
* @see #isRunning()
* @see #stop()
*/
+ @Override
public void start() {
if (!isRunning()) {
run();
@@ -127,6 +127,7 @@
* @see #isRunning()
* @see #start()
*/
+ @Override
public void stop() {
if (isRunning()) {
unscheduleSelf(this);
@@ -138,6 +139,7 @@
*
* @return true if the animation is running, false otherwise
*/
+ @Override
public boolean isRunning() {
return mAnimating;
}
@@ -148,6 +150,7 @@
*
* @see #start()
*/
+ @Override
public void run() {
nextFrame(false);
}
@@ -165,41 +168,41 @@
public int getNumberOfFrames() {
return mAnimationState.getChildCount();
}
-
+
/**
* @return The Drawable at the specified frame index
*/
public Drawable getFrame(int index) {
return mAnimationState.getChild(index);
}
-
+
/**
- * @return The duration in milliseconds of the frame at the
+ * @return The duration in milliseconds of the frame at the
* specified index
*/
public int getDuration(int i) {
return mAnimationState.mDurations[i];
}
-
+
/**
* @return True of the animation will play once, false otherwise
*/
public boolean isOneShot() {
return mAnimationState.mOneShot;
}
-
+
/**
* Sets whether the animation should play once or repeat.
- *
+ *
* @param oneShot Pass true if the animation should only play once
*/
public void setOneShot(boolean oneShot) {
mAnimationState.mOneShot = oneShot;
}
-
+
/**
* Add a frame to the animation
- *
+ *
* @param frame The frame to add
* @param duration How long in milliseconds the frame should appear
*/
@@ -209,7 +212,7 @@
setFrame(0, true, false);
}
}
-
+
private void nextFrame(boolean unschedule) {
int next = mCurFrame+1;
final int N = mAnimationState.getChildCount();
@@ -239,21 +242,21 @@
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
-
+
TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.AnimationDrawable);
super.inflateWithAttributes(r, parser, a,
com.android.internal.R.styleable.AnimationDrawable_visible);
-
+
mAnimationState.setVariablePadding(a.getBoolean(
com.android.internal.R.styleable.AnimationDrawable_variablePadding, false));
-
+
mAnimationState.mOneShot = a.getBoolean(
com.android.internal.R.styleable.AnimationDrawable_oneshot, false);
-
+
a.recycle();
-
+
int type;
final int innerDepth = parser.getDepth()+1;
@@ -267,7 +270,7 @@
if (depth > innerDepth || !parser.getName().equals("item")) {
continue;
}
-
+
a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawableItem);
int duration = a.getInt(
com.android.internal.R.styleable.AnimationDrawableItem_duration, -1);
@@ -278,9 +281,9 @@
}
int drawableRes = a.getResourceId(
com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0);
-
+
a.recycle();
-
+
Drawable dr;
if (drawableRes != 0) {
dr = r.getDrawable(drawableRes);
@@ -295,7 +298,7 @@
}
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
-
+
mAnimationState.addFrame(dr, duration);
if (dr != null) {
dr.setCallback(this);
@@ -342,7 +345,7 @@
}
public void addFrame(Drawable dr, int dur) {
- // Do not combine the following. The array index must be evaluated before
+ // Do not combine the following. The array index must be evaluated before
// the array is accessed because super.addChild(dr) has a side effect on mDurations.
int pos = super.addChild(dr);
mDurations[pos] = dur;
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index ef6c085..be940df 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -31,6 +31,7 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.ColorDrawable.ColorState;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Shader;
@@ -618,9 +619,11 @@
@Override
public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
- mBitmapState.mTint = tint;
- mBitmapState.mTintMode = tintMode;
- computeTintFilter();
+ final BitmapState state = mBitmapState;
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
invalidateSelf();
}
@@ -638,21 +641,6 @@
return mBitmapState.mTintMode;
}
- private void computeTintFilter() {
- final BitmapState state = mBitmapState;
- if (state.mTint != null && state.mTintMode != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- if (mTintFilter != null) {
- mTintFilter.setColor(color);
- mTintFilter.setMode(state.mTintMode);
- } else {
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- } else {
- mTintFilter = null;
- }
- }
-
/**
* @hide Candidate for future API inclusion
*/
@@ -679,17 +667,11 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mBitmapState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final BitmapState state = mBitmapState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
-
return false;
}
@@ -946,7 +928,7 @@
mTargetDensity = state.mTargetDensity;
}
- computeTintFilter();
+ updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
computeBitmapSize();
}
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 3ac9972..174de3a 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -19,10 +19,12 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -52,7 +54,7 @@
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
-
+
ClipDrawable() {
this(null, null);
}
@@ -111,6 +113,7 @@
// overrides from Drawable.Callback
+ @Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
@@ -118,6 +121,7 @@
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
@@ -125,6 +129,7 @@
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
@@ -169,6 +174,11 @@
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mClipState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mClipState.mDrawable.getOpacity();
}
@@ -197,7 +207,7 @@
@Override
public void draw(Canvas canvas) {
-
+
if (mClipState.mDrawable.getLevel() == 0) {
return;
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index df5ca33..3716182 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -17,6 +17,8 @@
package android.graphics.drawable;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -39,9 +41,13 @@
* @attr ref android.R.styleable#ColorDrawable_color
*/
public class ColorDrawable extends Drawable {
+ private final Paint mPaint = new Paint();
+
@ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
private ColorState mColorState;
- private final Paint mPaint = new Paint();
+ private ColorStateList mTint;
+ private PorterDuffColorFilter mTintFilter;
+
private boolean mMutated;
/**
@@ -84,9 +90,17 @@
@Override
public void draw(Canvas canvas) {
- if ((mColorState.mUseColor >>> 24) != 0) {
+ final ColorFilter colorFilter = mPaint.getColorFilter();
+ if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) {
+ if (colorFilter == null) {
+ mPaint.setColorFilter(mTintFilter);
+ }
+
mPaint.setColor(mColorState.mUseColor);
canvas.drawRect(getBounds(), mPaint);
+
+ // Restore original color filter.
+ mPaint.setColorFilter(colorFilter);
}
}
@@ -141,16 +155,51 @@
}
/**
- * Setting a color filter on a ColorDrawable has no effect.
+ * Sets the color filter applied to this color.
+ * <p>
+ * Only supported on version {@link android.os.Build.VERSION_CODES#L} and
+ * above. Calling this method has no effect on earlier versions.
*
- * @param colorFilter Ignore.
+ * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter)
*/
@Override
public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final ColorState state = mColorState;
+ if (state.mTint != tint || state.mTintMode != tintMode) {
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final ColorState state = mColorState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return mTint != null && mTint.isStateful();
}
@Override
public int getOpacity() {
+ if (mTintFilter != null || mPaint.getColorFilter() != null) {
+ return PixelFormat.TRANSLUCENT;
+ }
+
switch (mColorState.mUseColor >>> 24) {
case 255:
return PixelFormat.OPAQUE;
@@ -165,8 +214,7 @@
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(
- r, theme, attrs, R.styleable.ColorDrawable);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
inflateStateFromTypedArray(a);
a.recycle();
}
@@ -225,21 +273,25 @@
}
final static class ColorState extends ConstantState {
+ int[] mThemeAttrs;
int mBaseColor; // base color, independent of setAlpha()
@ViewDebug.ExportedProperty
int mUseColor; // basecolor modulated by setAlpha()
int mChangingConfigurations;
- int[] mThemeAttrs;
+ ColorStateList mTint;
+ Mode mTintMode;
ColorState() {
// Empty constructor.
}
ColorState(ColorState state) {
+ mThemeAttrs = state.mThemeAttrs;
mBaseColor = state.mBaseColor;
mUseColor = state.mUseColor;
mChangingConfigurations = state.mChangingConfigurations;
- mThemeAttrs = state.mThemeAttrs;
+ mTint = state.mTint;
+ mTintMode = state.mTintMode;
}
@Override
@@ -276,6 +328,6 @@
mColorState = state;
}
- // No local properties to initialize.
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 6a7757b..cb88e3d 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -17,13 +17,6 @@
package android.graphics.drawable;
import android.annotation.NonNull;
-import android.graphics.Insets;
-import android.graphics.Xfermode;
-import android.os.Trace;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -31,15 +24,19 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.PorterDuff.Mode;
+import android.graphics.Xfermode;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.StateSet;
@@ -47,6 +44,9 @@
import android.util.Xml;
import android.view.View;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1226,6 +1226,26 @@
}
/**
+ * Ensures the tint filter is consistent with the current tint color and
+ * mode.
+ */
+ PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
+ PorterDuff.Mode tintMode) {
+ if (tint == null || tintMode == null) {
+ return null;
+ }
+
+ final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
+ if (tintFilter == null) {
+ return new PorterDuffColorFilter(color, tintMode);
+ }
+
+ tintFilter.setColor(color);
+ tintFilter.setMode(tintMode);
+ return tintFilter;
+ }
+
+ /**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*/
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 2aef39f..8be6eb7 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.Canvas;
@@ -23,6 +24,7 @@
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.PorterDuff.Mode;
import android.os.SystemClock;
import android.util.LayoutDirection;
import android.util.SparseArray;
@@ -151,7 +153,7 @@
@Override
public void setColorFilter(ColorFilter cf) {
- mDrawableContainerState.mHasColorFilter = true;
+ mDrawableContainerState.mHasColorFilter = (cf != null);
if (mDrawableContainerState.mColorFilter != cf) {
mDrawableContainerState.mColorFilter = cf;
@@ -162,6 +164,20 @@
}
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mDrawableContainerState.mHasTint = (tint != null && tintMode != null);
+
+ if (mDrawableContainerState.mTint != tint || mDrawableContainerState.mTintMode != tintMode) {
+ mDrawableContainerState.mTint = tint;
+ mDrawableContainerState.mTintMode = tintMode;
+
+ if (mCurrDrawable != null) {
+ mCurrDrawable.mutate().setTint(tint, tintMode);
+ }
+ }
+ }
+
/**
* Change the global fade duration when a new drawable is entering
* the scene.
@@ -396,6 +412,8 @@
}
if (mDrawableContainerState.mHasColorFilter) {
d.setColorFilter(mDrawableContainerState.mColorFilter);
+ } else if (mDrawableContainerState.mHasTint) {
+ d.setTint(mDrawableContainerState.mTint, mDrawableContainerState.mTintMode);
}
d.setVisible(isVisible(), true);
d.setDither(mDrawableContainerState.mDither);
@@ -566,6 +584,10 @@
ColorFilter mColorFilter;
boolean mHasColorFilter;
+ ColorStateList mTint;
+ Mode mTintMode;
+ boolean mHasTint;
+
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
@@ -588,6 +610,9 @@
mAutoMirrored = orig.mAutoMirrored;
mColorFilter = orig.mColorFilter;
mHasColorFilter = orig.mHasColorFilter;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
+ mHasTint = orig.mHasTint;
// Cloning the following values may require creating futures.
mConstantPadding = orig.getConstantPadding();
@@ -741,7 +766,7 @@
final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- final Drawable d = drawables[i];
+ final Drawable d = drawables[i];
if (d != null) {
if (d.canApplyTheme()) {
return true;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 9e0ab86..220e81c 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -19,10 +19,12 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.util.Log;
@@ -44,8 +46,7 @@
* @attr ref android.R.styleable#InsetDrawable_insetTop
* @attr ref android.R.styleable#InsetDrawable_insetBottom
*/
-public class InsetDrawable extends Drawable implements Drawable.Callback
-{
+public class InsetDrawable extends Drawable implements Drawable.Callback {
// Most of this is copied from ScaleDrawable.
private InsetState mInsetState;
private final Rect mTmpRect = new Rect();
@@ -62,13 +63,13 @@
public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
int insetRight, int insetBottom) {
this(null, null);
-
+
mInsetState.mDrawable = drawable;
mInsetState.mInsetLeft = insetLeft;
mInsetState.mInsetTop = insetTop;
mInsetState.mInsetRight = insetRight;
mInsetState.mInsetBottom = insetBottom;
-
+
if (drawable != null) {
drawable.setCallback(this);
}
@@ -78,7 +79,7 @@
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
int type;
-
+
TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.InsetDrawable);
@@ -168,7 +169,7 @@
| mInsetState.mChangingConfigurations
| mInsetState.mDrawable.getChangingConfigurations();
}
-
+
@Override
public boolean getPadding(Rect padding) {
boolean pad = mInsetState.mDrawable.getPadding(padding);
@@ -178,7 +179,7 @@
padding.top += mInsetState.mInsetTop;
padding.bottom += mInsetState.mInsetBottom;
- if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
+ if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
return true;
} else {
@@ -217,6 +218,11 @@
mInsetState.mDrawable.setColorFilter(cf);
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mInsetState.mDrawable.setTint(tint, tintMode);
+ }
+
/** {@hide} */
@Override
public void setLayoutDirection(int layoutDirection) {
@@ -227,7 +233,7 @@
public int getOpacity() {
return mInsetState.mDrawable.getOpacity();
}
-
+
@Override
public boolean isStateful() {
return mInsetState.mDrawable.isStateful();
@@ -239,7 +245,12 @@
onBoundsChange(getBounds());
return changed;
}
-
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ return mInsetState.mDrawable.setLevel(level);
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
final Rect r = mTmpRect;
@@ -321,12 +332,12 @@
public Drawable newDrawable() {
return new InsetDrawable(this, null);
}
-
+
@Override
public Drawable newDrawable(Resources res) {
return new InsetDrawable(this, res);
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 27f0a9d..5cea7c9 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -16,12 +16,14 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
@@ -630,6 +632,15 @@
}
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i = 0; i < N; i++) {
+ array[i].mDrawable.setTint(tint, tintMode);
+ }
+ }
+
/**
* Sets the opacity of this drawable directly, instead of collecting the
* states from the layers
diff --git a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
index 9e56f67..c484094 100644
--- a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
+++ b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
@@ -27,8 +27,10 @@
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -67,6 +69,7 @@
private final Ring mRing;
private MaterialProgressState mState;
+ private PorterDuffColorFilter mTintFilter;
/** Canvas rotation in degrees. */
private float mRotation;
@@ -106,6 +109,8 @@
float insets = minEdge / 2.0f - state.mInnerRadius;
ring.setInsets(insets);
}
+
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
@Override
@@ -118,15 +123,21 @@
}
@Override
- protected boolean onStateChange(int[] state) {
- boolean changed = super.onStateChange(state);
+ protected boolean onStateChange(int[] stateSet) {
+ boolean changed = super.onStateChange(stateSet);
- final int color = mState.mColor.getColorForState(state, Color.TRANSPARENT);
+ final MaterialProgressState state = mState;
+ final int color = state.mColor.getColorForState(stateSet, Color.TRANSPARENT);
if (color != mRing.getColor()) {
mRing.setColor(color);
changed = true;
}
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ changed = true;
+ }
+
return changed;
}
@@ -223,11 +234,24 @@
return mRing.getColorFilter();
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ if (mState.mTint != tint || mState.mTintMode != tintMode) {
+ mState.mTint = tint;
+ mState.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
+ }
+ }
+
+ @SuppressWarnings("unused")
private void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}
+ @SuppressWarnings("unused")
private float getRotation() {
return mRotation;
}
@@ -331,6 +355,8 @@
private int mWidth = -1;
private int mHeight = -1;
private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT);
+ private ColorStateList mTint = null;
+ private Mode mTintMode = null;
public MaterialProgressState(MaterialProgressState orig) {
if (orig != null) {
@@ -340,6 +366,8 @@
mWidth = orig.mWidth;
mHeight = orig.mHeight;
mColor = orig.mColor;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
}
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index fea68ee..28335ea 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -327,25 +327,12 @@
@Override
public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
- mNinePatchState.mTint = tint;
- mNinePatchState.mTintMode = tintMode;
- computeTintFilter();
- invalidateSelf();
- }
-
- private void computeTintFilter() {
final NinePatchState state = mNinePatchState;
- if (state.mTint != null && state.mTintMode != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- if (mTintFilter != null) {
- mTintFilter.setColor(color);
- mTintFilter.setMode(state.mTintMode);
- } else {
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- } else {
- mTintFilter = null;
- }
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
}
@Override
@@ -549,15 +536,10 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mNinePatchState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final NinePatchState state = mNinePatchState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
return false;
@@ -689,7 +671,7 @@
mPadding = new Rect(state.mPadding);
}
- computeTintFilter();
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
setNinePatch(state.mNinePatch);
}
}
diff --git a/graphics/java/android/graphics/drawable/PaintDrawable.java b/graphics/java/android/graphics/drawable/PaintDrawable.java
index c71cda1..a82e7b9 100644
--- a/graphics/java/android/graphics/drawable/PaintDrawable.java
+++ b/graphics/java/android/graphics/drawable/PaintDrawable.java
@@ -35,7 +35,7 @@
public PaintDrawable(int color) {
getPaint().setColor(color);
}
-
+
/**
* Specify radius for the corners of the rectangle. If this is > 0, then the
* drawable is drawn in a round-rectangle, rather than a rectangle.
@@ -51,7 +51,7 @@
}
setCornerRadii(radii);
}
-
+
/**
* Specify radii for each of the 4 corners. For each corner, the array
* contains 2 values, [X_radius, Y_radius]. The corners are ordered
@@ -78,9 +78,9 @@
int radius = a.getDimensionPixelSize(
com.android.internal.R.styleable.DrawableCorners_radius, 0);
setCornerRadius(radius);
-
+
// now check of they have any per-corner radii
-
+
int topLeftRadius = a.getDimensionPixelSize(
com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius);
int topRightRadius = a.getDimensionPixelSize(
diff --git a/graphics/java/android/graphics/drawable/PictureDrawable.java b/graphics/java/android/graphics/drawable/PictureDrawable.java
index cb2d8f6..6dcda1f 100644
--- a/graphics/java/android/graphics/drawable/PictureDrawable.java
+++ b/graphics/java/android/graphics/drawable/PictureDrawable.java
@@ -25,7 +25,7 @@
/**
* Drawable subclass that wraps a Picture, allowing the picture to be used
- * whereever a Drawable is supported.
+ * wherever a Drawable is supported.
*/
public class PictureDrawable extends Drawable {
@@ -40,7 +40,7 @@
public PictureDrawable(Picture picture) {
mPicture = picture;
}
-
+
/**
* Return the picture associated with the drawable. May be null.
*
@@ -49,7 +49,7 @@
public Picture getPicture() {
return mPicture;
}
-
+
/**
* Associate a picture with this drawable. The picture may be null.
*
@@ -58,7 +58,7 @@
public void setPicture(Picture picture) {
mPicture = picture;
}
-
+
@Override
public void draw(Canvas canvas) {
if (mPicture != null) {
@@ -86,16 +86,16 @@
// not sure, so be safe
return PixelFormat.TRANSLUCENT;
}
-
+
@Override
public void setFilterBitmap(boolean filter) {}
-
+
@Override
public void setDither(boolean dither) {}
-
+
@Override
public void setColorFilter(ColorFilter colorFilter) {}
-
+
@Override
public void setAlpha(int alpha) {}
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 06aeb98..8f8fa98 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -22,6 +22,8 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -137,6 +139,11 @@
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index f090c11..46c92fe 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -19,10 +19,12 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -188,6 +190,11 @@
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mScaleState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mScaleState.mDrawable.getOpacity();
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 024f77c..beb300d 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -24,6 +24,7 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@@ -32,6 +33,7 @@
import android.content.res.Resources.Theme;
import android.util.AttributeSet;
+import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +76,7 @@
* ShapeDrawable constructor.
*/
public ShapeDrawable() {
- this((ShapeState) null);
+ this(new ShapeState(null), null, null);
}
/**
@@ -83,20 +85,11 @@
* @param s the Shape that this ShapeDrawable should be
*/
public ShapeDrawable(Shape s) {
- this((ShapeState) null);
+ this(new ShapeState(null), null, null);
mShapeState.mShape = s;
}
- private ShapeDrawable(ShapeState state) {
- mShapeState = new ShapeState(state);
-
- if (state != null && state.mTint != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- }
-
/**
* Returns the Shape of this ShapeDrawable.
*/
@@ -292,31 +285,13 @@
}
@Override
- public void setTint(ColorStateList tint, Mode tintMode) {
- if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
- mShapeState.mTint = tint;
- mShapeState.mTintMode = tintMode;
- updateTintFilter();
- invalidateSelf();
- }
- }
+ public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
+ final ShapeState state = mShapeState;
+ state.mTint = tint;
+ state.mTintMode = tintMode;
- /**
- * Ensures the tint filter is consistent with the current tint color and
- * mode.
- */
- private void updateTintFilter() {
- final ColorStateList tint = mShapeState.mTint;
- final Mode tintMode = mShapeState.mTintMode;
- if (tint != null && tintMode != null) {
- if (mTintFilter == null) {
- mTintFilter = new PorterDuffColorFilter(0, tintMode);
- } else {
- mTintFilter.setMode(tintMode);
- }
- } else {
- mTintFilter = null;
- }
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
}
@Override
@@ -357,17 +332,11 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mShapeState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final ShapeState state = mShapeState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
-
return false;
}
@@ -408,20 +377,8 @@
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable);
-
- int color = mShapeState.mPaint.getColor();
- color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color);
- mShapeState.mPaint.setColor(color);
-
- boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false);
- mShapeState.mPaint.setDither(dither);
-
- setIntrinsicWidth((int)
- a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f));
- setIntrinsicHeight((int)
- a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f));
-
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ShapeDrawable);
+ updateStateFromTypedArray(a);
a.recycle();
int type;
@@ -441,6 +398,38 @@
}
}
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final ShapeState state = mShapeState;
+ if (state == null || state.mThemeAttrs == null) {
+ return;
+ }
+
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ private void updateStateFromTypedArray(TypedArray a) {
+ final ShapeState state = mShapeState;
+ final Paint paint = state.mPaint;
+
+ int color = paint.getColor();
+ color = a.getColor(R.styleable.ShapeDrawable_color, color);
+ paint.setColor(color);
+
+ boolean dither = paint.isDither();
+ dither = a.getBoolean(R.styleable.ShapeDrawable_dither, dither);
+ paint.setDither(dither);
+
+ setIntrinsicWidth((int) a.getDimension(
+ R.styleable.ShapeDrawable_width, state.mIntrinsicWidth));
+ setIntrinsicHeight((int) a.getDimension(
+ R.styleable.ShapeDrawable_height, state.mIntrinsicHeight));
+ }
+
private void updateShape() {
if (mShapeState.mShape != null) {
final Rect r = getBounds();
@@ -498,6 +487,7 @@
* Defines the intrinsic properties of this ShapeDrawable's Shape.
*/
final static class ShapeState extends ConstantState {
+ int[] mThemeAttrs;
int mChangingConfigurations;
Paint mPaint;
Shape mShape;
@@ -511,6 +501,7 @@
ShapeState(ShapeState orig) {
if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
mPaint = orig.mPaint;
mShape = orig.mShape;
mTint = orig.mTint;
@@ -526,13 +517,23 @@
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Drawable newDrawable() {
- return new ShapeDrawable(this);
+ return new ShapeDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new ShapeDrawable(this);
+ return new ShapeDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new ShapeDrawable(this, res, theme);
}
@Override
@@ -541,6 +542,17 @@
}
}
+ private ShapeDrawable(ShapeState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mShapeState = new ShapeState(state);
+ applyTheme(theme);
+ } else {
+ mShapeState = state;
+ }
+
+ mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode);
+ }
+
/**
* Base class defines a factory object that is called each time the drawable
* is resized (has a new width or height). Its resize() method returns a
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index b2fac9b..f359fdd 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -235,10 +235,10 @@
public Drawable getStateDrawable(int index) {
return mStateListState.getChild(index);
}
-
+
/**
* Gets the index of the drawable with the provided state set.
- *
+ *
* @param stateSet the state set to look up
* @return the index of the provided state set, or -1 if not found
* @hide pending API council
@@ -248,7 +248,7 @@
public int getStateDrawableIndex(int[] stateSet) {
return mStateListState.indexOfStateSet(stateSet);
}
-
+
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 622e90b..4380ca4e 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -42,20 +42,20 @@
public class TransitionDrawable extends LayerDrawable implements Drawable.Callback {
/**
- * A transition is about to start.
+ * A transition is about to start.
*/
private static final int TRANSITION_STARTING = 0;
-
+
/**
* The transition has started and the animation is in progress
*/
private static final int TRANSITION_RUNNING = 1;
-
+
/**
* No transition will be applied
*/
private static final int TRANSITION_NONE = 2;
-
+
/**
* The current state of the transition. One of {@link #TRANSITION_STARTING},
* {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE}
@@ -101,10 +101,10 @@
LayerState createConstantState(LayerState state, Resources res) {
return new TransitionState((TransitionState) state, this, res);
}
-
+
/**
* Begin the second layer on top of the first layer.
- *
+ *
* @param durationMillis The length of the transition in milliseconds
*/
public void startTransition(int durationMillis) {
@@ -116,7 +116,7 @@
mTransitionState = TRANSITION_STARTING;
invalidateSelf();
}
-
+
/**
* Show only the first layer.
*/
@@ -184,7 +184,7 @@
}
break;
}
-
+
final int alpha = mAlpha;
final boolean crossFade = mCrossFade;
final ChildDrawable[] array = mLayerState.mChildren;
@@ -217,7 +217,7 @@
d.draw(canvas);
d.setAlpha(0xFF);
}
-
+
if (!done) {
invalidateSelf();
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c531c22..c3c1bca 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -14,6 +14,7 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -25,8 +26,10 @@
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.PorterDuff.Mode;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -137,6 +140,8 @@
private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
+ private PorterDuffColorFilter mTintFilter;
+
public VectorDrawable() {
mVectorState = new VectorDrawableState(null);
}
@@ -147,6 +152,9 @@
if (theme != null && canApplyTheme()) {
applyTheme(theme);
}
+
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
}
Object getTargetByName(String name) {
@@ -182,11 +190,46 @@
@Override
public void setColorFilter(ColorFilter colorFilter) {
- mVectorState.mVPathRenderer.setColorFilter(colorFilter);
+ final VectorDrawableState state = mVectorState;
+ if (colorFilter != null) {
+ // Color filter overrides tint.
+ mTintFilter = null;
+ } else if (state.mTint != null && state.mTintMode != null) {
+ // Restore the tint filter, if we need one.
+ final int color = state.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
+ colorFilter = mTintFilter;
+ }
+
+ state.mVPathRenderer.setColorFilter(colorFilter);
invalidateSelf();
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final VectorDrawableState state = mVectorState;
+ if (state.mTint != tint || state.mTintMode != tintMode) {
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final VectorDrawableState state = mVectorState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@@ -397,6 +440,8 @@
int mChangingConfigurations;
VPathRenderer mVPathRenderer;
Rect mPadding;
+ ColorStateList mTint;
+ Mode mTintMode;
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
@@ -404,6 +449,8 @@
// TODO: Make sure the constant state are handled correctly.
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
mPadding = new Rect(copy.mPadding);
+ mTint = copy.mTint;
+ mTintMode = copy.mTintMode;
}
}
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6fd9999..402f28b 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -565,11 +565,8 @@
// call, any texture operation will be performed on the default
// texture (name=0)
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
+ unbindTexture(texture);
+
glDeleteTextures(1, &texture);
}
@@ -577,6 +574,14 @@
memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
}
+void Caches::unbindTexture(GLuint texture) {
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b4b5927..83a5d9a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -261,6 +261,11 @@
void resetBoundTextures();
/**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 6a2ef2a..1002e13 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -185,6 +185,7 @@
}
void Layer::clearTexture() {
+ caches.unbindTexture(texture.id);
texture.id = 0;
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index df9aee5..a5fd375 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -334,8 +334,10 @@
void LayerRenderer::flushLayer(Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
+ if (!layer) return;
+
GLuint fbo = layer->getFbo();
- if (layer && fbo) {
+ if (fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Extensions::getInstance().hasDiscardFramebuffer()) {
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 60b4b96..1001cae0 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -288,19 +288,19 @@
switch (bitmap->config()) {
case SkBitmap::kA8_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case SkBitmap::kRGB_565_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case SkBitmap::kARGB_8888_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
// decoding happened
@@ -340,27 +340,49 @@
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
- GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
+ uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
+ width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
}
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
+ if ((stride == width) || useStride) {
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+ }
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
} else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
+ // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+ if (!temp) return;
+
+ uint8_t * pDst = (uint8_t *)temp;
+ uint8_t * pSrc = (uint8_t *)data;
+ for (GLsizei i = 0; i < height; i++) {
+ memcpy(pDst, pSrc, width * bpp);
+ pDst += width * bpp;
+ pSrc += stride * bpp;
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
+
+ free(temp);
}
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index e5b5c1a..61db5b0 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -142,7 +142,7 @@
void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6280fde..2f1e11e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,12 +50,6 @@
*/
public class AudioManager {
- // If we should use the new sessions APIs.
- private final static boolean USE_SESSIONS = true;
- // If we should use the legacy APIs. If both are true information will be
- // duplicated through both paths. Currently this flag isn't used.
- private final static boolean USE_LEGACY = true;
-
private final Context mContext;
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
@@ -483,17 +477,8 @@
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
*/
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendMediaButtonEvent(keyEvent, false);
- } else {
- IAudioService service = getService();
- try {
- service.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
- }
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendMediaButtonEvent(keyEvent, false);
}
/**
@@ -644,12 +629,8 @@
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
- } else {
- service.adjustVolume(direction, flags, mContext.getOpPackageName());
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -679,13 +660,8 @@
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
- } else {
- service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
- mContext.getOpPackageName());
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
@@ -1425,7 +1401,12 @@
* <var>false</var> to turn mute off
*/
public void setMicrophoneMute(boolean on){
- AudioSystem.muteMicrophone(on);
+ IAudioService service = getService();
+ try {
+ service.setMicrophoneMute(on, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setMicrophoneMute", e);
+ }
}
/**
@@ -2210,16 +2191,15 @@
}
IAudioService service = getService();
try {
- // pi != null
+ // pi != null, this is currently still needed to support across
+ // reboot launching of the last app.
service.registerMediaButtonIntent(pi, eventReceiver,
eventReceiver == null ? mToken : null);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.addMediaButtonListener(pi, mContext);
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.addMediaButtonListener(pi, mContext);
}
/**
@@ -2293,10 +2273,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
}
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.removeMediaButtonListener(pi);
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.removeMediaButtonListener(pi);
}
/**
@@ -2310,20 +2288,7 @@
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- IAudioService service = getService();
- try {
- int rcseId = service.registerRemoteControlClient(
- rcClient.getRcMediaIntent(), /* mediaIntent */
- rcClient.getIRemoteControlClient(),/* rcClient */
- // used to match media button event receiver and audio focus
- mContext.getPackageName()); /* packageName */
- rcClient.setRcseId(rcseId);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
- }
- if (USE_SESSIONS) {
- rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
- }
+ rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
/**
@@ -2336,16 +2301,7 @@
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent */
- rcClient.getIRemoteControlClient()); /* rcClient */
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
- }
- if (USE_SESSIONS) {
- rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
- }
+ rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
/**
@@ -2363,25 +2319,8 @@
if (rctlr == null) {
return false;
}
- if (USE_SESSIONS) {
- rctlr.startListeningToSessions();
- return true;
- } else {
- IAudioService service = getService();
- final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
- final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
- try {
- int[] artworkDimensions = rctlr.getArtworkSize();
- boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
- artworkDimensions[0]/* w */, artworkDimensions[1]/* h */,
- listenerComponent);
- rctlr.setIsRegistered(reg);
- return reg;
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteController " + e);
- return false;
- }
- }
+ rctlr.startListeningToSessions();
+ return true;
}
/**
@@ -2393,17 +2332,7 @@
if (rctlr == null) {
return;
}
- if (USE_SESSIONS) {
- rctlr.stopListeningToSessions();
- } else {
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
- rctlr.setIsRegistered(false);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
- }
- }
+ rctlr.stopListeningToSessions();
}
/**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2f782cc..72f4a58 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -108,8 +108,7 @@
/** Debug volumes */
protected static final boolean DEBUG_VOL = false;
- /** Reroute calls to media session apis */
- private static final boolean USE_SESSIONS = true;
+ /** debug calls to media session apis */
private static final boolean DEBUG_SESSIONS = true;
/** Allow volume changes to set ringer mode to silent? */
@@ -1437,6 +1436,16 @@
}
}
+ /** @see AudioManager#setMicrophoneMute(boolean) */
+ public void setMicrophoneMute(boolean on, String callingPackage) {
+ if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
+ AudioSystem.muteMicrophone(on);
+ }
+
/** @see AudioManager#getRingerMode() */
public int getRingerMode() {
synchronized(mSettingsLock) {
@@ -4474,27 +4483,19 @@
}
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
- } else {
- mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
}
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
- } else {
- mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
}
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
}
//==========================================================================================
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
new file mode 100644
index 0000000..ec33c5c
--- /dev/null
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -0,0 +1,1464 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.CharacterStyle;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
+import android.text.style.UpdateAppearance;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Vector;
+
+/** @hide */
+public class ClosedCaptionRenderer extends SubtitleController.Renderer {
+ private final Context mContext;
+ private ClosedCaptionWidget mRenderingWidget;
+
+ public ClosedCaptionRenderer(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public boolean supports(MediaFormat format) {
+ if (format.containsKey(MediaFormat.KEY_MIME)) {
+ return format.getString(MediaFormat.KEY_MIME).equals(
+ MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608);
+ }
+ return false;
+ }
+
+ @Override
+ public SubtitleTrack createTrack(MediaFormat format) {
+ if (mRenderingWidget == null) {
+ mRenderingWidget = new ClosedCaptionWidget(mContext);
+ }
+ return new ClosedCaptionTrack(mRenderingWidget, format);
+ }
+}
+
+/** @hide */
+class ClosedCaptionTrack extends SubtitleTrack {
+ private final ClosedCaptionWidget mRenderingWidget;
+ private final CCParser mCCParser;
+
+ ClosedCaptionTrack(ClosedCaptionWidget renderingWidget, MediaFormat format) {
+ super(format);
+
+ mRenderingWidget = renderingWidget;
+ mCCParser = new CCParser(renderingWidget);
+ }
+
+ @Override
+ public void onData(byte[] data, boolean eos, long runID) {
+ mCCParser.parse(data);
+ }
+
+ @Override
+ public RenderingWidget getRenderingWidget() {
+ return mRenderingWidget;
+ }
+
+ @Override
+ public void updateView(Vector<Cue> activeCues) {
+ // Overriding with NO-OP, CC rendering by-passes this
+ }
+}
+
+/**
+ * @hide
+ *
+ * CCParser processes CEA-608 closed caption data.
+ *
+ * It calls back into OnDisplayChangedListener upon
+ * display change with styled text for rendering.
+ *
+ */
+class CCParser {
+ public static final int MAX_ROWS = 15;
+ public static final int MAX_COLS = 32;
+
+ private static final String TAG = "CCParser";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final int INVALID = -1;
+
+ // EIA-CEA-608: Table 70 - Control Codes
+ private static final int RCL = 0x20;
+ private static final int BS = 0x21;
+ private static final int AOF = 0x22;
+ private static final int AON = 0x23;
+ private static final int DER = 0x24;
+ private static final int RU2 = 0x25;
+ private static final int RU3 = 0x26;
+ private static final int RU4 = 0x27;
+ private static final int FON = 0x28;
+ private static final int RDC = 0x29;
+ private static final int TR = 0x2a;
+ private static final int RTD = 0x2b;
+ private static final int EDM = 0x2c;
+ private static final int CR = 0x2d;
+ private static final int ENM = 0x2e;
+ private static final int EOC = 0x2f;
+
+ // Transparent Space
+ private static final char TS = '\u00A0';
+
+ // Captioning Modes
+ private static final int MODE_UNKNOWN = 0;
+ private static final int MODE_PAINT_ON = 1;
+ private static final int MODE_ROLL_UP = 2;
+ private static final int MODE_POP_ON = 3;
+ private static final int MODE_TEXT = 4;
+
+ private final DisplayListener mListener;
+
+ private int mMode = MODE_PAINT_ON;
+ private int mRollUpSize = 4;
+
+ private CCMemory mDisplay = new CCMemory();
+ private CCMemory mNonDisplay = new CCMemory();
+ private CCMemory mTextMem = new CCMemory();
+
+ CCParser(DisplayListener listener) {
+ mListener = listener;
+ }
+
+ void parse(byte[] data) {
+ CCData[] ccData = CCData.fromByteArray(data);
+
+ for (int i = 0; i < ccData.length; i++) {
+ if (DEBUG) {
+ Log.d(TAG, ccData[i].toString());
+ }
+
+ if (handleCtrlCode(ccData[i])
+ || handleTabOffsets(ccData[i])
+ || handlePACCode(ccData[i])
+ || handleMidRowCode(ccData[i])) {
+ continue;
+ }
+
+ handleDisplayableChars(ccData[i]);
+ }
+ }
+
+ interface DisplayListener {
+ public void onDisplayChanged(SpannableStringBuilder[] styledTexts);
+ public CaptionStyle getCaptionStyle();
+ }
+
+ private CCMemory getMemory() {
+ // get the CC memory to operate on for current mode
+ switch (mMode) {
+ case MODE_POP_ON:
+ return mNonDisplay;
+ case MODE_TEXT:
+ // TODO(chz): support only caption mode for now,
+ // in text mode, dump everything to text mem.
+ return mTextMem;
+ case MODE_PAINT_ON:
+ case MODE_ROLL_UP:
+ return mDisplay;
+ default:
+ Log.w(TAG, "unrecoginized mode: " + mMode);
+ }
+ return mDisplay;
+ }
+
+ private boolean handleDisplayableChars(CCData ccData) {
+ if (!ccData.isDisplayableChar()) {
+ return false;
+ }
+
+ // Extended char includes 1 automatic backspace
+ if (ccData.isExtendedChar()) {
+ getMemory().bs();
+ }
+
+ getMemory().writeText(ccData.getDisplayText());
+
+ if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) {
+ updateDisplay();
+ }
+
+ return true;
+ }
+
+ private boolean handleMidRowCode(CCData ccData) {
+ StyleCode m = ccData.getMidRow();
+ if (m != null) {
+ getMemory().writeMidRowCode(m);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean handlePACCode(CCData ccData) {
+ PAC pac = ccData.getPAC();
+
+ if (pac != null) {
+ if (mMode == MODE_ROLL_UP) {
+ getMemory().moveBaselineTo(pac.getRow(), mRollUpSize);
+ }
+ getMemory().writePAC(pac);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean handleTabOffsets(CCData ccData) {
+ int tabs = ccData.getTabOffset();
+
+ if (tabs > 0) {
+ getMemory().tab(tabs);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean handleCtrlCode(CCData ccData) {
+ int ctrlCode = ccData.getCtrlCode();
+ switch(ctrlCode) {
+ case RCL:
+ // select pop-on style
+ mMode = MODE_POP_ON;
+ break;
+ case BS:
+ getMemory().bs();
+ break;
+ case DER:
+ getMemory().der();
+ break;
+ case RU2:
+ case RU3:
+ case RU4:
+ mRollUpSize = (ctrlCode - 0x23);
+ // erase memory if currently in other style
+ if (mMode != MODE_ROLL_UP) {
+ mDisplay.erase();
+ mNonDisplay.erase();
+ }
+ // select roll-up style
+ mMode = MODE_ROLL_UP;
+ break;
+ case FON:
+ Log.i(TAG, "Flash On");
+ break;
+ case RDC:
+ // select paint-on style
+ mMode = MODE_PAINT_ON;
+ break;
+ case TR:
+ mMode = MODE_TEXT;
+ mTextMem.erase();
+ break;
+ case RTD:
+ mMode = MODE_TEXT;
+ break;
+ case EDM:
+ // erase display memory
+ mDisplay.erase();
+ updateDisplay();
+ break;
+ case CR:
+ if (mMode == MODE_ROLL_UP) {
+ getMemory().rollUp(mRollUpSize);
+ } else {
+ getMemory().cr();
+ }
+ if (mMode == MODE_ROLL_UP) {
+ updateDisplay();
+ }
+ break;
+ case ENM:
+ // erase non-display memory
+ mNonDisplay.erase();
+ break;
+ case EOC:
+ // swap display/non-display memory
+ swapMemory();
+ // switch to pop-on style
+ mMode = MODE_POP_ON;
+ updateDisplay();
+ break;
+ case INVALID:
+ default:
+ // not handled
+ return false;
+ }
+
+ // handled
+ return true;
+ }
+
+ private void updateDisplay() {
+ if (mListener != null) {
+ CaptionStyle captionStyle = mListener.getCaptionStyle();
+ mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle));
+ }
+ }
+
+ private void swapMemory() {
+ CCMemory temp = mDisplay;
+ mDisplay = mNonDisplay;
+ mNonDisplay = temp;
+ }
+
+ private static class StyleCode {
+ static final int COLOR_WHITE = 0;
+ static final int COLOR_GREEN = 1;
+ static final int COLOR_BLUE = 2;
+ static final int COLOR_CYAN = 3;
+ static final int COLOR_RED = 4;
+ static final int COLOR_YELLOW = 5;
+ static final int COLOR_MAGENTA = 6;
+ static final int COLOR_INVALID = 7;
+
+ static final int STYLE_ITALICS = 0x00000001;
+ static final int STYLE_UNDERLINE = 0x00000002;
+
+ static final String[] mColorMap = {
+ "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID"
+ };
+
+ final int mStyle;
+ final int mColor;
+
+ static StyleCode fromByte(byte data2) {
+ int style = 0;
+ int color = (data2 >> 1) & 0x7;
+
+ if ((data2 & 0x1) != 0) {
+ style |= STYLE_UNDERLINE;
+ }
+
+ if (color == COLOR_INVALID) {
+ // WHITE ITALICS
+ color = COLOR_WHITE;
+ style |= STYLE_ITALICS;
+ }
+
+ return new StyleCode(style, color);
+ }
+
+ StyleCode(int style, int color) {
+ mStyle = style;
+ mColor = color;
+ }
+
+ boolean isItalics() {
+ return (mStyle & STYLE_ITALICS) != 0;
+ }
+
+ boolean isUnderline() {
+ return (mStyle & STYLE_UNDERLINE) != 0;
+ }
+
+ int getColor() {
+ return mColor;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ str.append("{");
+ str.append(mColorMap[mColor]);
+ if ((mStyle & STYLE_ITALICS) != 0) {
+ str.append(", ITALICS");
+ }
+ if ((mStyle & STYLE_UNDERLINE) != 0) {
+ str.append(", UNDERLINE");
+ }
+ str.append("}");
+
+ return str.toString();
+ }
+ }
+
+ private static class PAC extends StyleCode {
+ final int mRow;
+ final int mCol;
+
+ static PAC fromBytes(byte data1, byte data2) {
+ int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9};
+ int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5);
+ int style = 0;
+ if ((data2 & 1) != 0) {
+ style |= STYLE_UNDERLINE;
+ }
+ if ((data2 & 0x10) != 0) {
+ // indent code
+ int indent = (data2 >> 1) & 0x7;
+ return new PAC(row, indent * 4, style, COLOR_WHITE);
+ } else {
+ // style code
+ int color = (data2 >> 1) & 0x7;
+
+ if (color == COLOR_INVALID) {
+ // WHITE ITALICS
+ color = COLOR_WHITE;
+ style |= STYLE_ITALICS;
+ }
+ return new PAC(row, -1, style, color);
+ }
+ }
+
+ PAC(int row, int col, int style, int color) {
+ super(style, color);
+ mRow = row;
+ mCol = col;
+ }
+
+ boolean isIndentPAC() {
+ return (mCol >= 0);
+ }
+
+ int getRow() {
+ return mRow;
+ }
+
+ int getCol() {
+ return mCol;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%d, %d}, %s",
+ mRow, mCol, super.toString());
+ }
+ }
+
+ /* CCLineBuilder keeps track of displayable chars, as well as
+ * MidRow styles and PACs, for a single line of CC memory.
+ *
+ * It generates styled text via getStyledText() method.
+ */
+ private static class CCLineBuilder {
+ private final StringBuilder mDisplayChars;
+ private final StyleCode[] mMidRowStyles;
+ private final StyleCode[] mPACStyles;
+
+ CCLineBuilder(String str) {
+ mDisplayChars = new StringBuilder(str);
+ mMidRowStyles = new StyleCode[mDisplayChars.length()];
+ mPACStyles = new StyleCode[mDisplayChars.length()];
+ }
+
+ void setCharAt(int index, char ch) {
+ mDisplayChars.setCharAt(index, ch);
+ mMidRowStyles[index] = null;
+ }
+
+ void setMidRowAt(int index, StyleCode m) {
+ mDisplayChars.setCharAt(index, ' ');
+ mMidRowStyles[index] = m;
+ }
+
+ void setPACAt(int index, PAC pac) {
+ mPACStyles[index] = pac;
+ }
+
+ char charAt(int index) {
+ return mDisplayChars.charAt(index);
+ }
+
+ int length() {
+ return mDisplayChars.length();
+ }
+
+ void applyStyleSpan(
+ SpannableStringBuilder styledText,
+ StyleCode s, int start, int end) {
+ if (s.isItalics()) {
+ styledText.setSpan(
+ new StyleSpan(android.graphics.Typeface.ITALIC),
+ start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (s.isUnderline()) {
+ styledText.setSpan(
+ new UnderlineSpan(),
+ start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ SpannableStringBuilder getStyledText(CaptionStyle captionStyle) {
+ SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars);
+ int start = -1, next = 0;
+ int styleStart = -1;
+ StyleCode curStyle = null;
+ while (next < mDisplayChars.length()) {
+ StyleCode newStyle = null;
+ if (mMidRowStyles[next] != null) {
+ // apply mid-row style change
+ newStyle = mMidRowStyles[next];
+ } else if (mPACStyles[next] != null
+ && (styleStart < 0 || start < 0)) {
+ // apply PAC style change, only if:
+ // 1. no style set, or
+ // 2. style set, but prev char is none-displayable
+ newStyle = mPACStyles[next];
+ }
+ if (newStyle != null) {
+ curStyle = newStyle;
+ if (styleStart >= 0 && start >= 0) {
+ applyStyleSpan(styledText, newStyle, styleStart, next);
+ }
+ styleStart = next;
+ }
+
+ if (mDisplayChars.charAt(next) != TS) {
+ if (start < 0) {
+ start = next;
+ }
+ } else if (start >= 0) {
+ int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1;
+ int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1;
+ styledText.setSpan(
+ new MutableBackgroundColorSpan(captionStyle.backgroundColor),
+ expandedStart, expandedEnd,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (styleStart >= 0) {
+ applyStyleSpan(styledText, curStyle, styleStart, expandedEnd);
+ }
+ start = -1;
+ }
+ next++;
+ }
+
+ return styledText;
+ }
+ }
+
+ /*
+ * CCMemory models a console-style display.
+ */
+ private static class CCMemory {
+ private final String mBlankLine;
+ private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2];
+ private int mRow;
+ private int mCol;
+
+ CCMemory() {
+ char[] blank = new char[MAX_COLS + 2];
+ Arrays.fill(blank, TS);
+ mBlankLine = new String(blank);
+ }
+
+ void erase() {
+ // erase all lines
+ for (int i = 0; i < mLines.length; i++) {
+ mLines[i] = null;
+ }
+ mRow = MAX_ROWS;
+ mCol = 1;
+ }
+
+ void der() {
+ if (mLines[mRow] != null) {
+ for (int i = 0; i < mCol; i++) {
+ if (mLines[mRow].charAt(i) != TS) {
+ for (int j = mCol; j < mLines[mRow].length(); j++) {
+ mLines[j].setCharAt(j, TS);
+ }
+ return;
+ }
+ }
+ mLines[mRow] = null;
+ }
+ }
+
+ void tab(int tabs) {
+ moveCursorByCol(tabs);
+ }
+
+ void bs() {
+ moveCursorByCol(-1);
+ if (mLines[mRow] != null) {
+ mLines[mRow].setCharAt(mCol, TS);
+ if (mCol == MAX_COLS - 1) {
+ // Spec recommendation:
+ // if cursor was at col 32, move cursor
+ // back to col 31 and erase both col 31&32
+ mLines[mRow].setCharAt(MAX_COLS, TS);
+ }
+ }
+ }
+
+ void cr() {
+ moveCursorTo(mRow + 1, 1);
+ }
+
+ void rollUp(int windowSize) {
+ int i;
+ for (i = 0; i <= mRow - windowSize; i++) {
+ mLines[i] = null;
+ }
+ int startRow = mRow - windowSize + 1;
+ if (startRow < 1) {
+ startRow = 1;
+ }
+ for (i = startRow; i < mRow; i++) {
+ mLines[i] = mLines[i + 1];
+ }
+ for (i = mRow; i < mLines.length; i++) {
+ // clear base row
+ mLines[i] = null;
+ }
+ // default to col 1, in case PAC is not sent
+ mCol = 1;
+ }
+
+ void writeText(String text) {
+ for (int i = 0; i < text.length(); i++) {
+ getLineBuffer(mRow).setCharAt(mCol, text.charAt(i));
+ moveCursorByCol(1);
+ }
+ }
+
+ void writeMidRowCode(StyleCode m) {
+ getLineBuffer(mRow).setMidRowAt(mCol, m);
+ moveCursorByCol(1);
+ }
+
+ void writePAC(PAC pac) {
+ if (pac.isIndentPAC()) {
+ moveCursorTo(pac.getRow(), pac.getCol());
+ } else {
+ moveCursorToRow(pac.getRow());
+ }
+ getLineBuffer(mRow).setPACAt(mCol, pac);
+ }
+
+ SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) {
+ ArrayList<SpannableStringBuilder> rows =
+ new ArrayList<SpannableStringBuilder>(MAX_ROWS);
+ for (int i = 1; i <= MAX_ROWS; i++) {
+ rows.add(mLines[i] != null ?
+ mLines[i].getStyledText(captionStyle) : null);
+ }
+ return rows.toArray(new SpannableStringBuilder[MAX_ROWS]);
+ }
+
+ private static int clamp(int x, int min, int max) {
+ return x < min ? min : (x > max ? max : x);
+ }
+
+ private void moveCursorTo(int row, int col) {
+ mRow = clamp(row, 1, MAX_ROWS);
+ mCol = clamp(col, 1, MAX_COLS);
+ }
+
+ private void moveCursorToRow(int row) {
+ mRow = clamp(row, 1, MAX_ROWS);
+ }
+
+ private void moveCursorByCol(int col) {
+ mCol = clamp(mCol + col, 1, MAX_COLS);
+ }
+
+ private void moveBaselineTo(int baseRow, int windowSize) {
+ if (mRow == baseRow) {
+ return;
+ }
+ int actualWindowSize = windowSize;
+ if (baseRow < actualWindowSize) {
+ actualWindowSize = baseRow;
+ }
+ if (mRow < actualWindowSize) {
+ actualWindowSize = mRow;
+ }
+
+ int i;
+ if (baseRow < mRow) {
+ // copy from bottom to top row
+ for (i = actualWindowSize - 1; i >= 0; i--) {
+ mLines[baseRow - i] = mLines[mRow - i];
+ }
+ } else {
+ // copy from top to bottom row
+ for (i = 0; i < actualWindowSize; i++) {
+ mLines[baseRow - i] = mLines[mRow - i];
+ }
+ }
+ // clear rest of the rows
+ for (i = 0; i <= baseRow - windowSize; i++) {
+ mLines[i] = null;
+ }
+ for (i = baseRow + 1; i < mLines.length; i++) {
+ mLines[i] = null;
+ }
+ }
+
+ private CCLineBuilder getLineBuffer(int row) {
+ if (mLines[row] == null) {
+ mLines[row] = new CCLineBuilder(mBlankLine);
+ }
+ return mLines[row];
+ }
+ }
+
+ /*
+ * CCData parses the raw CC byte pair into displayable chars,
+ * misc control codes, Mid-Row or Preamble Address Codes.
+ */
+ private static class CCData {
+ private final byte mType;
+ private final byte mData1;
+ private final byte mData2;
+
+ private static final String[] mCtrlCodeMap = {
+ "RCL", "BS" , "AOF", "AON",
+ "DER", "RU2", "RU3", "RU4",
+ "FON", "RDC", "TR" , "RTD",
+ "EDM", "CR" , "ENM", "EOC",
+ };
+
+ private static final String[] mSpecialCharMap = {
+ "\u00AE",
+ "\u00B0",
+ "\u00BD",
+ "\u00BF",
+ "\u2122",
+ "\u00A2",
+ "\u00A3",
+ "\u266A", // Eighth note
+ "\u00E0",
+ "\u00A0", // Transparent space
+ "\u00E8",
+ "\u00E2",
+ "\u00EA",
+ "\u00EE",
+ "\u00F4",
+ "\u00FB",
+ };
+
+ private static final String[] mSpanishCharMap = {
+ // Spanish and misc chars
+ "\u00C1", // A
+ "\u00C9", // E
+ "\u00D3", // I
+ "\u00DA", // O
+ "\u00DC", // U
+ "\u00FC", // u
+ "\u2018", // opening single quote
+ "\u00A1", // inverted exclamation mark
+ "*",
+ "'",
+ "\u2014", // em dash
+ "\u00A9", // Copyright
+ "\u2120", // Servicemark
+ "\u2022", // round bullet
+ "\u201C", // opening double quote
+ "\u201D", // closing double quote
+ // French
+ "\u00C0",
+ "\u00C2",
+ "\u00C7",
+ "\u00C8",
+ "\u00CA",
+ "\u00CB",
+ "\u00EB",
+ "\u00CE",
+ "\u00CF",
+ "\u00EF",
+ "\u00D4",
+ "\u00D9",
+ "\u00F9",
+ "\u00DB",
+ "\u00AB",
+ "\u00BB"
+ };
+
+ private static final String[] mProtugueseCharMap = {
+ // Portuguese
+ "\u00C3",
+ "\u00E3",
+ "\u00CD",
+ "\u00CC",
+ "\u00EC",
+ "\u00D2",
+ "\u00F2",
+ "\u00D5",
+ "\u00F5",
+ "{",
+ "}",
+ "\\",
+ "^",
+ "_",
+ "|",
+ "~",
+ // German and misc chars
+ "\u00C4",
+ "\u00E4",
+ "\u00D6",
+ "\u00F6",
+ "\u00DF",
+ "\u00A5",
+ "\u00A4",
+ "\u2502", // vertical bar
+ "\u00C5",
+ "\u00E5",
+ "\u00D8",
+ "\u00F8",
+ "\u250C", // top-left corner
+ "\u2510", // top-right corner
+ "\u2514", // lower-left corner
+ "\u2518", // lower-right corner
+ };
+
+ static CCData[] fromByteArray(byte[] data) {
+ CCData[] ccData = new CCData[data.length / 3];
+
+ for (int i = 0; i < ccData.length; i++) {
+ ccData[i] = new CCData(
+ data[i * 3],
+ data[i * 3 + 1],
+ data[i * 3 + 2]);
+ }
+
+ return ccData;
+ }
+
+ CCData(byte type, byte data1, byte data2) {
+ mType = type;
+ mData1 = data1;
+ mData2 = data2;
+ }
+
+ int getCtrlCode() {
+ if ((mData1 == 0x14 || mData1 == 0x1c)
+ && mData2 >= 0x20 && mData2 <= 0x2f) {
+ return mData2;
+ }
+ return INVALID;
+ }
+
+ StyleCode getMidRow() {
+ // only support standard Mid-row codes, ignore
+ // optional background/foreground mid-row codes
+ if ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x20 && mData2 <= 0x2f) {
+ return StyleCode.fromByte(mData2);
+ }
+ return null;
+ }
+
+ PAC getPAC() {
+ if ((mData1 & 0x70) == 0x10
+ && (mData2 & 0x40) == 0x40
+ && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) {
+ return PAC.fromBytes(mData1, mData2);
+ }
+ return null;
+ }
+
+ int getTabOffset() {
+ if ((mData1 == 0x17 || mData1 == 0x1f)
+ && mData2 >= 0x21 && mData2 <= 0x23) {
+ return mData2 & 0x3;
+ }
+ return 0;
+ }
+
+ boolean isDisplayableChar() {
+ return isBasicChar() || isSpecialChar() || isExtendedChar();
+ }
+
+ String getDisplayText() {
+ String str = getBasicChars();
+
+ if (str == null) {
+ str = getSpecialChar();
+
+ if (str == null) {
+ str = getExtendedChar();
+ }
+ }
+
+ return str;
+ }
+
+ private String ctrlCodeToString(int ctrlCode) {
+ return mCtrlCodeMap[ctrlCode - 0x20];
+ }
+
+ private boolean isBasicChar() {
+ return mData1 >= 0x20 && mData1 <= 0x7f;
+ }
+
+ private boolean isSpecialChar() {
+ return ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x30 && mData2 <= 0x3f);
+ }
+
+ private boolean isExtendedChar() {
+ return ((mData1 == 0x12 || mData1 == 0x1A
+ || mData1 == 0x13 || mData1 == 0x1B)
+ && mData2 >= 0x20 && mData2 <= 0x3f);
+ }
+
+ private char getBasicChar(byte data) {
+ char c;
+ // replace the non-ASCII ones
+ switch (data) {
+ case 0x2A: c = '\u00E1'; break;
+ case 0x5C: c = '\u00E9'; break;
+ case 0x5E: c = '\u00ED'; break;
+ case 0x5F: c = '\u00F3'; break;
+ case 0x60: c = '\u00FA'; break;
+ case 0x7B: c = '\u00E7'; break;
+ case 0x7C: c = '\u00F7'; break;
+ case 0x7D: c = '\u00D1'; break;
+ case 0x7E: c = '\u00F1'; break;
+ case 0x7F: c = '\u2588'; break; // Full block
+ default: c = (char) data; break;
+ }
+ return c;
+ }
+
+ private String getBasicChars() {
+ if (mData1 >= 0x20 && mData1 <= 0x7f) {
+ StringBuilder builder = new StringBuilder(2);
+ builder.append(getBasicChar(mData1));
+ if (mData2 >= 0x20 && mData2 <= 0x7f) {
+ builder.append(getBasicChar(mData2));
+ }
+ return builder.toString();
+ }
+
+ return null;
+ }
+
+ private String getSpecialChar() {
+ if ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x30 && mData2 <= 0x3f) {
+ return mSpecialCharMap[mData2 - 0x30];
+ }
+
+ return null;
+ }
+
+ private String getExtendedChar() {
+ if ((mData1 == 0x12 || mData1 == 0x1A)
+ && mData2 >= 0x20 && mData2 <= 0x3f){
+ // 1 Spanish/French char
+ return mSpanishCharMap[mData2 - 0x20];
+ } else if ((mData1 == 0x13 || mData1 == 0x1B)
+ && mData2 >= 0x20 && mData2 <= 0x3f){
+ // 1 Portuguese/German/Danish char
+ return mProtugueseCharMap[mData2 - 0x20];
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ String str;
+
+ if (mData1 < 0x10 && mData2 < 0x10) {
+ // Null Pad, ignore
+ return String.format("[%d]Null: %02x %02x", mType, mData1, mData2);
+ }
+
+ int ctrlCode = getCtrlCode();
+ if (ctrlCode != INVALID) {
+ return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode));
+ }
+
+ int tabOffset = getTabOffset();
+ if (tabOffset > 0) {
+ return String.format("[%d]Tab%d", mType, tabOffset);
+ }
+
+ PAC pac = getPAC();
+ if (pac != null) {
+ return String.format("[%d]PAC: %s", mType, pac.toString());
+ }
+
+ StyleCode m = getMidRow();
+ if (m != null) {
+ return String.format("[%d]Mid-row: %s", mType, m.toString());
+ }
+
+ if (isDisplayableChar()) {
+ return String.format("[%d]Displayable: %s (%02x %02x)",
+ mType, getDisplayText(), mData1, mData2);
+ }
+
+ return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2);
+ }
+ }
+}
+
+/**
+ * @hide
+ *
+ * MutableBackgroundColorSpan
+ *
+ * This is a mutable version of BackgroundSpan to facilitate text
+ * rendering with edge styles.
+ *
+ */
+class MutableBackgroundColorSpan extends CharacterStyle
+ implements UpdateAppearance, ParcelableSpan {
+ private int mColor;
+
+ public MutableBackgroundColorSpan(int color) {
+ mColor = color;
+ }
+ public MutableBackgroundColorSpan(Parcel src) {
+ mColor = src.readInt();
+ }
+ public void setBackgroundColor(int color) {
+ mColor = color;
+ }
+ public int getBackgroundColor() {
+ return mColor;
+ }
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.BACKGROUND_COLOR_SPAN;
+ }
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mColor);
+ }
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ ds.bgColor = mColor;
+ }
+}
+
+/**
+ * Widget capable of rendering CEA-608 closed captions.
+ *
+ * @hide
+ */
+class ClosedCaptionWidget extends ViewGroup implements
+ SubtitleTrack.RenderingWidget,
+ CCParser.DisplayListener {
+ private static final String TAG = "ClosedCaptionWidget";
+
+ private static final Rect mTextBounds = new Rect();
+ private static final String mDummyText = "1234567890123456789012345678901234";
+ private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
+
+ /** Captioning manager, used to obtain and track caption properties. */
+ private final CaptioningManager mManager;
+
+ /** Callback for rendering changes. */
+ private OnChangedListener mListener;
+
+ /** Current caption style. */
+ private CaptionStyle mCaptionStyle;
+
+ /* Closed caption layout. */
+ private CCLayout mClosedCaptionLayout;
+
+ /** Whether a caption style change listener is registered. */
+ private boolean mHasChangeListener;
+
+ public ClosedCaptionWidget(Context context) {
+ this(context, null);
+ }
+
+ public ClosedCaptionWidget(Context context, AttributeSet attrs) {
+ this(context, null, 0);
+ }
+
+ public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Cannot render text over video when layer type is hardware.
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
+ mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle());
+
+ mClosedCaptionLayout = new CCLayout(context);
+ mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
+ addView(mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+
+ requestLayout();
+ }
+
+ @Override
+ public void setOnChangedListener(OnChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+
+ measure(widthSpec, heightSpec);
+ layout(0, 0, width, height);
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ if (visible) {
+ setVisibility(View.VISIBLE);
+ } else {
+ setVisibility(View.GONE);
+ }
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onDisplayChanged(SpannableStringBuilder[] styledTexts) {
+ mClosedCaptionLayout.update(styledTexts);
+
+ if (mListener != null) {
+ mListener.onChanged(this);
+ }
+ }
+
+ @Override
+ public CaptionStyle getCaptionStyle() {
+ return mCaptionStyle;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mClosedCaptionLayout.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mClosedCaptionLayout.layout(l, t, r, b);
+ }
+
+ /**
+ * Manages whether this renderer is listening for caption style changes.
+ */
+ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() {
+ @Override
+ public void onUserStyleChanged(CaptionStyle userStyle) {
+ mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle);
+ mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
+ }
+ };
+
+ private void manageChangeListener() {
+ final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE;
+ if (mHasChangeListener != needsListener) {
+ mHasChangeListener = needsListener;
+
+ if (needsListener) {
+ mManager.addCaptioningChangeListener(mCaptioningListener);
+ } else {
+ mManager.removeCaptioningChangeListener(mCaptioningListener);
+ }
+ }
+ }
+
+ private static class CCLineBox extends TextView {
+ private static final float FONT_PADDING_RATIO = 0.75f;
+ private static final float EDGE_OUTLINE_RATIO = 0.1f;
+ private static final float EDGE_SHADOW_RATIO = 0.05f;
+ private float mOutlineWidth;
+ private float mShadowRadius;
+ private float mShadowOffset;
+
+ private int mTextColor = Color.WHITE;
+ private int mBgColor = Color.BLACK;
+ private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
+ private int mEdgeColor = Color.TRANSPARENT;
+
+ CCLineBox(Context context) {
+ super(context);
+ setGravity(Gravity.CENTER);
+ setBackgroundColor(Color.TRANSPARENT);
+ setTextColor(Color.WHITE);
+ setTypeface(Typeface.MONOSPACE);
+ setVisibility(View.INVISIBLE);
+
+ final Resources res = getContext().getResources();
+
+ // get the default (will be updated later during measure)
+ mOutlineWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_outline_width);
+ mShadowRadius = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_shadow_radius);
+ mShadowOffset = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_shadow_offset);
+ }
+
+ void setCaptionStyle(CaptionStyle captionStyle) {
+ mTextColor = captionStyle.foregroundColor;
+ mBgColor = captionStyle.backgroundColor;
+ mEdgeType = captionStyle.edgeType;
+ mEdgeColor = captionStyle.edgeColor;
+
+ setTextColor(mTextColor);
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
+ } else {
+ setShadowLayer(0, 0, 0, 0);
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ float fontSize = MeasureSpec.getSize(heightMeasureSpec)
+ * FONT_PADDING_RATIO;
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+
+ mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f;
+ mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;;
+ mShadowOffset = mShadowRadius;
+
+ // set font scale in the X direction to match the required width
+ setScaleX(1.0f);
+ getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds);
+ float actualTextWidth = mTextBounds.width();
+ float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec);
+ setScaleX(requiredTextWidth / actualTextWidth);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onDraw(Canvas c) {
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED
+ || mEdgeType == CaptionStyle.EDGE_TYPE_NONE
+ || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ // these edge styles don't require a second pass
+ super.onDraw(c);
+ return;
+ }
+
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
+ drawEdgeOutline(c);
+ } else {
+ // Raised or depressed
+ drawEdgeRaisedOrDepressed(c);
+ }
+ }
+
+ private void drawEdgeOutline(Canvas c) {
+ TextPaint textPaint = getPaint();
+
+ Paint.Style previousStyle = textPaint.getStyle();
+ Paint.Join previousJoin = textPaint.getStrokeJoin();
+ float previousWidth = textPaint.getStrokeWidth();
+
+ setTextColor(mEdgeColor);
+ textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ textPaint.setStrokeJoin(Paint.Join.ROUND);
+ textPaint.setStrokeWidth(mOutlineWidth);
+
+ // Draw outline and background only.
+ super.onDraw(c);
+
+ // Restore original settings.
+ setTextColor(mTextColor);
+ textPaint.setStyle(previousStyle);
+ textPaint.setStrokeJoin(previousJoin);
+ textPaint.setStrokeWidth(previousWidth);
+
+ // Remove the background.
+ setBackgroundSpans(Color.TRANSPARENT);
+ // Draw foreground only.
+ super.onDraw(c);
+ // Restore the background.
+ setBackgroundSpans(mBgColor);
+ }
+
+ private void drawEdgeRaisedOrDepressed(Canvas c) {
+ TextPaint textPaint = getPaint();
+
+ Paint.Style previousStyle = textPaint.getStyle();
+ textPaint.setStyle(Paint.Style.FILL);
+
+ final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED;
+ final int colorUp = raised ? Color.WHITE : mEdgeColor;
+ final int colorDown = raised ? mEdgeColor : Color.WHITE;
+ final float offset = mShadowRadius / 2f;
+
+ // Draw background and text with shadow up
+ setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
+ super.onDraw(c);
+
+ // Remove the background.
+ setBackgroundSpans(Color.TRANSPARENT);
+
+ // Draw text with shadow down
+ setShadowLayer(mShadowRadius, +offset, +offset, colorDown);
+ super.onDraw(c);
+
+ // Restore settings
+ textPaint.setStyle(previousStyle);
+
+ // Restore the background.
+ setBackgroundSpans(mBgColor);
+ }
+
+ private void setBackgroundSpans(int color) {
+ CharSequence text = getText();
+ if (text instanceof Spannable) {
+ Spannable spannable = (Spannable) text;
+ MutableBackgroundColorSpan[] bgSpans = spannable.getSpans(
+ 0, spannable.length(), MutableBackgroundColorSpan.class);
+ for (int i = 0; i < bgSpans.length; i++) {
+ bgSpans[i].setBackgroundColor(color);
+ }
+ }
+ }
+ }
+
+ private static class CCLayout extends LinearLayout {
+ private static final int MAX_ROWS = CCParser.MAX_ROWS;
+ private static final float SAFE_AREA_RATIO = 0.9f;
+
+ private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS];
+
+ CCLayout(Context context) {
+ super(context);
+ setGravity(Gravity.START);
+ setOrientation(LinearLayout.VERTICAL);
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i] = new CCLineBox(getContext());
+ addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ void setCaptionStyle(CaptionStyle captionStyle) {
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].setCaptionStyle(captionStyle);
+ }
+ }
+
+ void update(SpannableStringBuilder[] textBuffer) {
+ for (int i = 0; i < MAX_ROWS; i++) {
+ if (textBuffer[i] != null) {
+ mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE);
+ mLineBoxes[i].setVisibility(View.VISIBLE);
+ } else {
+ mLineBoxes[i].setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int safeWidth = getMeasuredWidth();
+ int safeHeight = getMeasuredHeight();
+
+ // CEA-608 assumes 4:3 video
+ if (safeWidth * 3 >= safeHeight * 4) {
+ safeWidth = safeHeight * 4 / 3;
+ } else {
+ safeHeight = safeWidth * 3 / 4;
+ }
+ safeWidth *= SAFE_AREA_RATIO;
+ safeHeight *= SAFE_AREA_RATIO;
+
+ int lineHeight = safeHeight / MAX_ROWS;
+ int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ lineHeight, MeasureSpec.EXACTLY);
+ int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ safeWidth, MeasureSpec.EXACTLY);
+
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // safe caption area
+ int viewPortWidth = r - l;
+ int viewPortHeight = b - t;
+ int safeWidth, safeHeight;
+ // CEA-608 assumes 4:3 video
+ if (viewPortWidth * 3 >= viewPortHeight * 4) {
+ safeWidth = viewPortHeight * 4 / 3;
+ safeHeight = viewPortHeight;
+ } else {
+ safeWidth = viewPortWidth;
+ safeHeight = viewPortWidth * 3 / 4;
+ }
+ safeWidth *= SAFE_AREA_RATIO;
+ safeHeight *= SAFE_AREA_RATIO;
+ int left = (viewPortWidth - safeWidth) / 2;
+ int top = (viewPortHeight - safeHeight) / 2;
+
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].layout(
+ left,
+ top + safeHeight * i / MAX_ROWS,
+ left + safeWidth,
+ top + safeHeight * (i + 1) / MAX_ROWS);
+ }
+ }
+ }
+};
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ba3cfb6..c29e967 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -78,6 +78,8 @@
int getLastAudibleMasterVolume();
+ void setMicrophoneMute(boolean on, String callingPackage);
+
void setRingerMode(int ringerMode);
int getRingerMode();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f258063..22db344 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -782,7 +782,7 @@
private void postEventFromNative(
int what, int arg1, int arg2, Object obj) {
- if (mEventHandler != null) {
+ if (mEventHandler != null && mNotificationCallback != null) {
Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj);
mEventHandler.sendMessage(msg);
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1b92410..e25714a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1649,8 +1649,8 @@
mFormat = MediaFormat.createSubtitleFormat(
MEDIA_MIMETYPE_TEXT_SUBRIP, language);
} else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- mFormat = MediaFormat.createSubtitleFormat(
- MEDIA_MIMETYPE_TEXT_VTT, language);
+ String mime = in.readString();
+ mFormat = MediaFormat.createSubtitleFormat(mime, language);
mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
@@ -1683,12 +1683,40 @@
dest.writeString(getLanguage());
if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+ dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
}
}
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append(getClass().getName());
+ out.append('{');
+ switch (mTrackType) {
+ case MEDIA_TRACK_TYPE_VIDEO:
+ out.append("VIDEO");
+ break;
+ case MEDIA_TRACK_TYPE_AUDIO:
+ out.append("AUDIO");
+ break;
+ case MEDIA_TRACK_TYPE_TIMEDTEXT:
+ out.append("TIMEDTEXT");
+ break;
+ case MEDIA_TRACK_TYPE_SUBTITLE:
+ out.append("SUBTITLE");
+ break;
+ default:
+ out.append("UNKNOWN");
+ break;
+ }
+ out.append(", " + mFormat.toString());
+ out.append("}");
+ return out.toString();
+ }
+
/**
* Used to read a TrackInfo from a Parcel.
*/
@@ -1757,6 +1785,12 @@
*/
public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
+ /**
+ * MIME type for CEA-608 closed caption data.
+ * @hide
+ */
+ public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+
/*
* A helper function to check if the mime type is supported by media framework.
*/
@@ -1792,16 +1826,11 @@
}
SubtitleTrack track = mInbandSubtitleTracks[index];
if (track != null) {
- try {
- long runID = data.getStartTimeUs() + 1;
- // TODO: move conversion into track
- track.onData(new String(data.getData(), "UTF-8"), true /* eos */, runID);
- track.setRunDiscardTimeMs(
- runID,
- (data.getStartTimeUs() + data.getDurationUs()) / 1000);
- } catch (java.io.UnsupportedEncodingException e) {
- Log.w(TAG, "subtitle data for track " + index + " is not UTF-8 encoded: " + e);
- }
+ long runID = data.getStartTimeUs() + 1;
+ track.onData(data.getData(), true /* eos */, runID);
+ track.setRunDiscardTimeMs(
+ runID,
+ (data.getStartTimeUs() + data.getDurationUs()) / 1000);
}
}
};
@@ -1872,7 +1901,7 @@
}
scanner.close();
mOutOfBandSubtitleTracks.add(track);
- track.onData(contents, true /* eos */, ~0 /* runID: keep forever */);
+ track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */);
return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 1da0215..ddd5b72 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -60,7 +60,6 @@
public class MediaRouter {
private static final String TAG = "MediaRouter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean USE_SESSIONS = true;
static class Static implements DisplayManager.DisplayListener {
final Context mAppContext;
@@ -2104,11 +2103,7 @@
public void setPlaybackType(int type) {
if (mPlaybackType != type) {
mPlaybackType = type;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
- }
+ configureSessionVolume();
}
}
@@ -2121,12 +2116,7 @@
public void setVolumeHandling(int volumeHandling) {
if (mVolumeHandling != volumeHandling) {
mVolumeHandling = volumeHandling;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
- }
+ configureSessionVolume();
}
}
@@ -2139,12 +2129,8 @@
volume = Math.max(0, Math.min(volume, getVolumeMax()));
if (mVolume != volume) {
mVolume = volume;
- if (USE_SESSIONS) {
- if (mSvp != null) {
- mSvp.notifyVolumeChanged();
- }
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ if (mSvp != null) {
+ mSvp.notifyVolumeChanged();
}
dispatchRouteVolumeChanged(this);
if (mGroup != null) {
@@ -2184,11 +2170,7 @@
public void setVolumeMax(int volumeMax) {
if (mVolumeMax != volumeMax) {
mVolumeMax = volumeMax;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
- }
+ configureSessionVolume();
}
}
@@ -2199,40 +2181,12 @@
public void setPlaybackStream(int stream) {
if (mPlaybackStream != stream) {
mPlaybackStream = stream;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
- }
+ configureSessionVolume();
}
}
private void updatePlaybackInfoOnRcc() {
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- if ((mRcc != null)
- && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
- // let AudioService know whom to call when remote volume
- // needs to be updated
- try {
- sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
- mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
- } catch (RemoteException e) {
- Log.e(TAG, "Error registering remote volume observer", e);
- }
- }
- }
+ configureSessionVolume();
}
private void configureSessionVolume() {
@@ -2272,12 +2226,6 @@
}
}
- private void setPlaybackInfoOnRcc(int what, int value) {
- if (mRcc != null) {
- mRcc.setPlaybackInformation(what, value);
- }
- }
-
class SessionVolumeProvider extends RemoteVolumeProvider {
public SessionVolumeProvider(int volumeControl, int maxVolume) {
diff --git a/media/java/android/media/PlayerRecord.java b/media/java/android/media/PlayerRecord.java
index f9708c3..664ddcf 100644
--- a/media/java/android/media/PlayerRecord.java
+++ b/media/java/android/media/PlayerRecord.java
@@ -56,7 +56,7 @@
*/
final private ComponentName mReceiverComponent;
- private int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ private int mRccId = -1;
/**
* A non-null token implies this record tracks a "live" player whose death is being monitored.
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 0caea5f..73bc61a 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -561,6 +561,8 @@
return;
}
synchronized (mCacheLock) {
+ // Still build the old metadata so when creating a new editor
+ // you get the expected values.
// assign the edited data
mMetadata = new Bundle(mEditorMetadata);
// add the information about editable keys
@@ -570,16 +572,6 @@
}
mOriginalArtwork = mEditorArtwork;
mEditorArtwork = null;
- if (mMetadataChanged & mArtworkChanged) {
- // send to remote control display if conditions are met
- sendMetadataWithArtwork_syncCacheLock(null, 0, 0);
- } else if (mMetadataChanged) {
- // send to remote control display if conditions are met
- sendMetadata_syncCacheLock(null);
- } else if (mArtworkChanged) {
- // send to remote control display if conditions are met
- sendArtwork_syncCacheLock(null, 0, 0);
- }
// USE_SESSIONS
if (mSession != null && mMetadataBuilder != null) {
@@ -687,14 +679,6 @@
// keep track of when the state change occurred
mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
- // send to remote control display if conditions are met
- sendPlaybackState_syncCacheLock(null);
- // update AudioService
- sendAudioServiceNewPlaybackState_syncCacheLock();
-
- // handle automatic playback position refreshes
- initiateCheckForDrift_syncCacheLock();
-
// USE_SESSIONS
if (mSession != null) {
int pbState = PlaybackState.getStateFromRccState(state);
@@ -707,29 +691,7 @@
}
}
- private void initiateCheckForDrift_syncCacheLock() {
- if (mEventHandler == null) {
- return;
- }
- mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
- if (!mNeedsPositionSync) {
- return;
- }
- if (mPlaybackPositionMs < 0) {
- // the current playback state has no known playback position, it's no use
- // trying to see if there is any drift at this point
- // (this also bypasses this mechanism for older apps that use the old
- // setPlaybackState(int) API)
- return;
- }
- if (playbackPositionShouldMove(mPlaybackState)) {
- // playback position moving, schedule next position drift check
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- getCheckPeriodFromSpeed(mPlaybackSpeed));
- }
- }
-
+ // TODO investigate if we still need position drift checking
private void onPositionDriftCheck() {
if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
synchronized(mCacheLock) {
@@ -781,9 +743,6 @@
// store locally
mTransportControlFlags = transportControlFlags;
- // send to remote control display if conditions are met
- sendTransportControlInfo_syncCacheLock(null);
-
// USE_SESSIONS
if (mSession != null) {
mSessionPlaybackState.setActions(PlaybackState
@@ -866,17 +825,7 @@
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
synchronized(mCacheLock) {
- int oldCapa = mPlaybackPositionCapabilities;
- if (l != null) {
- mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE;
- } else {
- mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE;
- }
mPositionUpdateListener = l;
- if (oldCapa != mPlaybackPositionCapabilities) {
- // tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock(null);
- }
}
}
@@ -888,17 +837,7 @@
*/
public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
- int oldCapa = mPlaybackPositionCapabilities;
- if (l != null) {
- mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE;
- } else {
- mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE;
- }
mPositionProvider = l;
- if (oldCapa != mPlaybackPositionCapabilities) {
- // tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock(null);
- }
if ((mPositionProvider != null) && (mEventHandler != null)
&& playbackPositionShouldMove(mPlaybackState)) {
// playback position is already moving, but now we have a position provider,
@@ -925,124 +864,12 @@
*/
public static int MEDIA_POSITION_WRITABLE = 1 << 1;
- private int mPlaybackPositionCapabilities = 0;
-
/** @hide */
public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
/** @hide */
// hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC]
public final static int DEFAULT_PLAYBACK_VOLUME = 15;
- private int mPlaybackType = PLAYBACK_TYPE_LOCAL;
- private int mPlaybackVolumeMax = DEFAULT_PLAYBACK_VOLUME;
- private int mPlaybackVolume = DEFAULT_PLAYBACK_VOLUME;
- private int mPlaybackVolumeHandling = DEFAULT_PLAYBACK_VOLUME_HANDLING;
- private int mPlaybackStream = AudioManager.STREAM_MUSIC;
-
- /**
- * @hide
- * Set information describing information related to the playback of media so the system
- * can implement additional behavior to handle non-local playback usecases.
- * @param what a key to specify the type of information to set. Valid keys are
- * {@link #PLAYBACKINFO_PLAYBACK_TYPE},
- * {@link #PLAYBACKINFO_USES_STREAM},
- * {@link #PLAYBACKINFO_VOLUME},
- * {@link #PLAYBACKINFO_VOLUME_MAX},
- * and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
- * @param value the value for the supplied information to set.
- */
- public void setPlaybackInformation(int what, int value) {
- synchronized(mCacheLock) {
- switch (what) {
- case PLAYBACKINFO_PLAYBACK_TYPE:
- if ((value >= PLAYBACK_TYPE_MIN) && (value <= PLAYBACK_TYPE_MAX)) {
- if (mPlaybackType != value) {
- mPlaybackType = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_PLAYBACK_TYPE");
- }
- break;
- case PLAYBACKINFO_VOLUME:
- if ((value > -1) && (value <= mPlaybackVolumeMax)) {
- if (mPlaybackVolume != value) {
- mPlaybackVolume = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME");
- }
- break;
- case PLAYBACKINFO_VOLUME_MAX:
- if (value > 0) {
- if (mPlaybackVolumeMax != value) {
- mPlaybackVolumeMax = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_MAX");
- }
- break;
- case PLAYBACKINFO_USES_STREAM:
- if ((value >= 0) && (value < AudioSystem.getNumStreamTypes())) {
- mPlaybackStream = value;
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_USES_STREAM");
- }
- break;
- case PLAYBACKINFO_VOLUME_HANDLING:
- if ((value >= PLAYBACK_VOLUME_FIXED) && (value <= PLAYBACK_VOLUME_VARIABLE)) {
- if (mPlaybackVolumeHandling != value) {
- mPlaybackVolumeHandling = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_HANDLING");
- }
- break;
- default:
- // not throwing an exception or returning an error if more keys are to be
- // supported in the future
- Log.w(TAG, "setPlaybackInformation() ignoring unknown key " + what);
- break;
- }
- }
- }
-
- /**
- * @hide
- * Return playback information represented as an integer value.
- * @param what a key to specify the type of information to retrieve. Valid keys are
- * {@link #PLAYBACKINFO_PLAYBACK_TYPE},
- * {@link #PLAYBACKINFO_USES_STREAM},
- * {@link #PLAYBACKINFO_VOLUME},
- * {@link #PLAYBACKINFO_VOLUME_MAX},
- * and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
- * @return the current value for the given information type, or
- * {@link #PLAYBACKINFO_INVALID_VALUE} if an error occurred or the request is invalid, or
- * the value is unknown.
- */
- public int getIntPlaybackInformation(int what) {
- synchronized(mCacheLock) {
- switch (what) {
- case PLAYBACKINFO_PLAYBACK_TYPE:
- return mPlaybackType;
- case PLAYBACKINFO_VOLUME:
- return mPlaybackVolume;
- case PLAYBACKINFO_VOLUME_MAX:
- return mPlaybackVolumeMax;
- case PLAYBACKINFO_USES_STREAM:
- return mPlaybackStream;
- case PLAYBACKINFO_VOLUME_HANDLING:
- return mPlaybackVolumeHandling;
- default:
- Log.e(TAG, "getIntPlaybackInformation() unknown key " + what);
- return PLAYBACKINFO_INVALID_VALUE;
- }
- }
- }
-
/**
* Lock for all cached data
*/
@@ -1102,13 +929,6 @@
* The current remote control client generation ID across the system, as known by this object
*/
private int mCurrentClientGenId = -1;
- /**
- * The remote control client generation ID, the last time it was told it was the current RC.
- * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control
- * client is the "focused" one, and that whenever this client's info is updated, it needs to
- * send it to the known IRemoteControlDisplay interfaces.
- */
- private int mInternalClientGenId = -2;
/**
* The media button intent description associated with this remote control client
@@ -1134,186 +954,18 @@
private MediaMetadata mMediaMetadata;
/**
- * A class to encapsulate all the information about a remote control display.
- * A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay
- */
- private class DisplayInfoForClient {
- /** may never be null */
- private IRemoteControlDisplay mRcDisplay;
- private int mArtworkExpectedWidth;
- private int mArtworkExpectedHeight;
- private boolean mWantsPositionSync = false;
- private boolean mEnabled = true;
-
- DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) {
- mRcDisplay = rcd;
- mArtworkExpectedWidth = w;
- mArtworkExpectedHeight = h;
- }
- }
-
- /**
- * The list of remote control displays to which this client will send information.
- * Accessed and modified synchronized on mCacheLock
- */
- private ArrayList<DisplayInfoForClient> mRcDisplays = new ArrayList<DisplayInfoForClient>(1);
-
- /**
* @hide
* Accessor to media button intent description (includes target component)
*/
public PendingIntent getRcMediaIntent() {
return mRcMediaIntent;
}
- /**
- * @hide
- * Accessor to IRemoteControlClient
- */
- public IRemoteControlClient getIRemoteControlClient() {
- return mIRCC;
- }
-
- /**
- * The IRemoteControlClient implementation
- */
- private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
-
- //TODO change name to informationRequestForAllDisplays()
- public void onInformationRequested(int generationId, int infoFlags) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- // signal new client
- mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN,
- /*arg1*/ generationId, /*arg2, ignored*/ 0));
- // send the information
- mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE);
- mEventHandler.removeMessages(MSG_REQUEST_METADATA);
- mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
- mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
- mEventHandler.removeMessages(MSG_REQUEST_METADATA_ARTWORK);
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, null));
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, null));
- mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK,
- 0, 0, null));
- }
- }
-
- public void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, rcd));
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, rcd));
- if ((w > 0) && (h > 0)) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, w, h, rcd));
- } else {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_METADATA, rcd));
- }
- }
- }
-
- public void setCurrentClientGenerationId(int clientGeneration) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
- }
- }
-
- public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_PLUG_DISPLAY, w, h, rcd));
- }
- }
-
- public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UNPLUG_DISPLAY, rcd));
- }
- }
-
- public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd));
- }
- }
-
- public void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_DISPLAY_WANTS_POS_SYNC, wantsSync ? 1 : 0, 0/*arg2 ignored*/, rcd));
- }
- }
-
- public void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_DISPLAY_ENABLE, enabled ? 1 : 0, 0/*arg2 ignored*/, rcd));
- }
- }
-
- public void seekTo(int generationId, long timeMs) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.removeMessages(MSG_SEEK_TO);
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */,
- new Long(timeMs)));
- }
- }
-
- public void updateMetadata(int generationId, int key, Rating value) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_METADATA, generationId /* arg1 */, key /* arg2*/, value));
- }
- }
- };
/**
* @hide
* Default value for the unique identifier
*/
public final static int RCSE_ID_UNREGISTERED = -1;
- /**
- * Unique identifier of the RemoteControlStackEntry in AudioService with which
- * this RemoteControlClient is associated.
- */
- private int mRcseId = RCSE_ID_UNREGISTERED;
- /**
- * @hide
- * To be only used by AudioManager after it has received the unique id from
- * IAudioService.registerRemoteControlClient()
- * @param id the unique identifier of the RemoteControlStackEntry in AudioService with which
- * this RemoteControlClient is associated.
- */
- public void setRcseId(int id) {
- mRcseId = id;
- }
-
- /**
- * @hide
- */
- public int getRcseId() {
- return mRcseId;
- }
// USE_SESSIONS
private MediaSession.TransportControlsCallback mTransportListener
@@ -1327,31 +979,13 @@
@Override
public void onSetRating(Rating rating) {
if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
- if (mEventHandler != null) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_METADATA, mCurrentClientGenId,
- MetadataEditor.RATING_KEY_BY_USER, rating));
- }
+ onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating);
}
}
};
private EventHandler mEventHandler;
- private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
- private final static int MSG_REQUEST_METADATA = 2;
- private final static int MSG_REQUEST_TRANSPORTCONTROL = 3;
- private final static int MSG_REQUEST_ARTWORK = 4;
- private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5;
- private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6;
- private final static int MSG_PLUG_DISPLAY = 7;
- private final static int MSG_UNPLUG_DISPLAY = 8;
- private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9;
- private final static int MSG_SEEK_TO = 10;
private final static int MSG_POSITION_DRIFT_CHECK = 11;
- private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;
- private final static int MSG_UPDATE_METADATA = 13;
- private final static int MSG_REQUEST_METADATA_ARTWORK = 14;
- private final static int MSG_DISPLAY_ENABLE = 15;
private class EventHandler extends Handler {
public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1361,63 +995,9 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_REQUEST_PLAYBACK_STATE:
- synchronized (mCacheLock) {
- sendPlaybackState_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_METADATA:
- synchronized (mCacheLock) {
- sendMetadata_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_TRANSPORTCONTROL:
- synchronized (mCacheLock) {
- sendTransportControlInfo_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_ARTWORK:
- synchronized (mCacheLock) {
- sendArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
- msg.arg1, msg.arg2);
- }
- break;
- case MSG_REQUEST_METADATA_ARTWORK:
- synchronized (mCacheLock) {
- sendMetadataWithArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
- msg.arg1, msg.arg2);
- }
- break;
- case MSG_NEW_INTERNAL_CLIENT_GEN:
- onNewInternalClientGen(msg.arg1);
- break;
- case MSG_NEW_CURRENT_CLIENT_GEN:
- onNewCurrentClientGen(msg.arg1);
- break;
- case MSG_PLUG_DISPLAY:
- onPlugDisplay((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
- break;
- case MSG_UNPLUG_DISPLAY:
- onUnplugDisplay((IRemoteControlDisplay)msg.obj);
- break;
- case MSG_UPDATE_DISPLAY_ARTWORK_SIZE:
- onUpdateDisplayArtworkSize((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
- break;
- case MSG_SEEK_TO:
- onSeekTo(msg.arg1, ((Long)msg.obj).longValue());
- break;
case MSG_POSITION_DRIFT_CHECK:
onPositionDriftCheck();
break;
- case MSG_DISPLAY_WANTS_POS_SYNC:
- onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
- break;
- case MSG_UPDATE_METADATA:
- onUpdateMetadata(msg.arg1, msg.arg2, msg.obj);
- break;
- case MSG_DISPLAY_ENABLE:
- onDisplayEnable((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
- break;
default:
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
}
@@ -1425,346 +1005,8 @@
}
//===========================================================
- // Communication with the IRemoteControlDisplay (the displays known to the system)
-
- private void sendPlaybackState_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setPlaybackState(mInternalClientGenId,
- mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
- mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setPlaybackState() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setPlaybackState(mInternalClientGenId,
- mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
- mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendMetadata_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setMetadata(mInternalClientGenId, mMetadata);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setMetadata() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendTransportControlInfo_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setTransportControlInfo(mInternalClientGenId,
- mTransportControlFlags, mPlaybackPositionCapabilities);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setTransportControlFlags() for dead display " + target,
- e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setTransportControlInfo(mInternalClientGenId,
- mTransportControlFlags, mPlaybackPositionCapabilities);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay,
- e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
- // FIXME modify to cache all requested sizes?
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- final DisplayInfoForClient di = new DisplayInfoForClient(target, w, h);
- sendArtworkToDisplay(di);
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- if (!sendArtworkToDisplay(displayIterator.next())) {
- displayIterator.remove();
- }
- }
- }
- }
-
- /**
- * Send artwork to an IRemoteControlDisplay.
- * @param di encapsulates the IRemoteControlDisplay that will receive the artwork, and its
- * dimension requirements.
- * @return false if there was an error communicating with the IRemoteControlDisplay.
- */
- private boolean sendArtworkToDisplay(DisplayInfoForClient di) {
- if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
- di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
- try {
- di.mRcDisplay.setArtwork(mInternalClientGenId, artwork);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in sendArtworkToDisplay(), dead display " + di.mRcDisplay, e);
- return false;
- }
- }
- return true;
- }
-
- private void sendMetadataWithArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
- // FIXME modify to cache all requested sizes?
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- if ((w > 0) && (h > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, w, h);
- target.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
- } else {
- target.setMetadata(mInternalClientGenId, mMetadata);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error in set(All)Metadata() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- try {
- if (di.mEnabled) {
- if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
- di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
- di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
- } else {
- di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error when setting metadata, dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
-
- //===========================================================
- // Communication with AudioService
-
- private static IAudioService sService;
-
- private static IAudioService getService()
- {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
- sService = IAudioService.Stub.asInterface(b);
- return sService;
- }
-
- private void sendAudioServiceNewPlaybackInfo_syncCacheLock(int what, int value) {
- if (mRcseId == RCSE_ID_UNREGISTERED) {
- return;
- }
- //Log.d(TAG, "sending to AudioService key=" + what + ", value=" + value);
- IAudioService service = getService();
- try {
- service.setPlaybackInfoForRcc(mRcseId, what, value);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e);
- }
- }
-
- private void sendAudioServiceNewPlaybackState_syncCacheLock() {
- if (mRcseId == RCSE_ID_UNREGISTERED) {
- return;
- }
- IAudioService service = getService();
- try {
- service.setPlaybackStateForRcc(mRcseId,
- mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setPlaybackStateForRcc", e);
- }
- }
-
- //===========================================================
// Message handlers
- private void onNewInternalClientGen(int clientGeneration) {
- synchronized (mCacheLock) {
- // this remote control client is told it is the "focused" one:
- // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true
- mInternalClientGenId = clientGeneration;
- }
- }
-
- private void onNewCurrentClientGen(int clientGeneration) {
- synchronized (mCacheLock) {
- mCurrentClientGenId = clientGeneration;
- }
- }
-
- /** pre-condition rcd != null */
- private void onPlugDisplay(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mCacheLock) {
- // do we have this display already?
- boolean displayKnown = false;
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext() && !displayKnown) {
- final DisplayInfoForClient di = displayIterator.next();
- displayKnown = di.mRcDisplay.asBinder().equals(rcd.asBinder());
- if (displayKnown) {
- // this display was known but the change in artwork size will cause the
- // artwork to be refreshed
- if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- if (!sendArtworkToDisplay(di)) {
- displayIterator.remove();
- }
- }
- }
- }
- if (!displayKnown) {
- mRcDisplays.add(new DisplayInfoForClient(rcd, w, h));
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onUnplugDisplay(IRemoteControlDisplay rcd) {
- synchronized(mCacheLock) {
- Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- displayIterator.remove();
- break;
- }
- }
- // list of RCDs has changed, reevaluate whether position check is still needed
- boolean oldNeedsPositionSync = mNeedsPositionSync;
- boolean newNeedsPositionSync = false;
- displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mWantsPositionSync) {
- newNeedsPositionSync = true;
- break;
- }
- }
- mNeedsPositionSync = newNeedsPositionSync;
- if (oldNeedsPositionSync != mNeedsPositionSync) {
- // update needed?
- initiateCheckForDrift_syncCacheLock();
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onUpdateDisplayArtworkSize(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mCacheLock) {
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder()) &&
- ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- if (di.mEnabled) {
- if (!sendArtworkToDisplay(di)) {
- displayIterator.remove();
- }
- }
- break;
- }
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onDisplayWantsSync(IRemoteControlDisplay rcd, boolean wantsSync) {
- synchronized(mCacheLock) {
- boolean oldNeedsPositionSync = mNeedsPositionSync;
- boolean newNeedsPositionSync = false;
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- // go through the list of RCDs and for each entry, check both whether this is the RCD
- // that gets upated, and whether the list has one entry that wants position sync
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mWantsPositionSync = wantsSync;
- }
- if (di.mWantsPositionSync) {
- newNeedsPositionSync = true;
- }
- }
- }
- mNeedsPositionSync = newNeedsPositionSync;
- if (oldNeedsPositionSync != mNeedsPositionSync) {
- // update needed?
- initiateCheckForDrift_syncCacheLock();
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onDisplayEnable(IRemoteControlDisplay rcd, boolean enable) {
- synchronized(mCacheLock) {
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mEnabled = enable;
- }
- }
- }
- }
-
private void onSeekTo(int generationId, long timeMs) {
synchronized (mCacheLock) {
if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) {
@@ -1785,42 +1027,6 @@
// Internal utilities
/**
- * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap.
- * If the bitmap fits, then do nothing and return the original.
- *
- * @param bitmap
- * @param maxWidth
- * @param maxHeight
- * @return
- */
-
- private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) {
- if (bitmap != null) {
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- if (width > maxWidth || height > maxHeight) {
- float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
- int newWidth = Math.round(scale * width);
- int newHeight = Math.round(scale * height);
- Bitmap.Config newConfig = bitmap.getConfig();
- if (newConfig == null) {
- newConfig = Bitmap.Config.ARGB_8888;
- }
- Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, newConfig);
- Canvas canvas = new Canvas(outBitmap);
- Paint paint = new Paint();
- paint.setAntiAlias(true);
- paint.setFilterBitmap(true);
- canvas.drawBitmap(bitmap, null,
- new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
- bitmap = outBitmap;
- }
- }
- return bitmap;
- }
-
-
- /**
* Returns whether, for the given playback state, the playback position is expected to
* be changing.
* @param playstate the playback state to evaluate
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index 06063de..b0e182d 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -83,7 +83,7 @@
* indicating the last section of the run. Calls from different
* runs must not be intermixed.
*
- * @param data
+ * @param data subtitle data byte buffer
* @param eos true if this is the last section of the run.
* @param runID mostly-unique ID for this run of data. Subtitle cues
* with runID of 0 are discarded immediately after
@@ -92,10 +92,8 @@
* with other runID-s are discarded at the end of the
* run, which defaults to the latest timestamp of
* any of its cues (with this runID).
- *
- * TODO use ByteBuffer
*/
- public abstract void onData(String data, boolean eos, long runID);
+ public abstract void onData(byte[] data, boolean eos, long runID);
/**
* Called when adding the subtitle rendering widget to the view hierarchy,
diff --git a/media/java/android/media/TtmlRenderer.java b/media/java/android/media/TtmlRenderer.java
index 0309334..75133c9 100644
--- a/media/java/android/media/TtmlRenderer.java
+++ b/media/java/android/media/TtmlRenderer.java
@@ -563,28 +563,35 @@
}
@Override
- public void onData(String data, boolean eos, long runID) {
- // implement intermixing restriction for TTML.
- synchronized(mParser) {
- if (mCurrentRunID != null && runID != mCurrentRunID) {
- throw new IllegalStateException(
- "Run #" + mCurrentRunID +
- " in progress. Cannot process run #" + runID);
- }
- mCurrentRunID = runID;
- mParsingData += data;
- if (eos) {
- try {
- mParser.parse(mParsingData, mCurrentRunID);
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ public void onData(byte[] data, boolean eos, long runID) {
+ try {
+ // TODO: handle UTF-8 conversion properly
+ String str = new String(data, "UTF-8");
+
+ // implement intermixing restriction for TTML.
+ synchronized(mParser) {
+ if (mCurrentRunID != null && runID != mCurrentRunID) {
+ throw new IllegalStateException(
+ "Run #" + mCurrentRunID +
+ " in progress. Cannot process run #" + runID);
}
- finishedRun(runID);
- mParsingData = "";
- mCurrentRunID = null;
+ mCurrentRunID = runID;
+ mParsingData += str;
+ if (eos) {
+ try {
+ mParser.parse(mParsingData, mCurrentRunID);
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ finishedRun(runID);
+ mParsingData = "";
+ mCurrentRunID = null;
+ }
}
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
}
}
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index 7977988..a9374d5 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -1001,22 +1001,28 @@
}
@Override
- public void onData(String data, boolean eos, long runID) {
- // implement intermixing restriction for WebVTT only for now
- synchronized(mParser) {
- if (mCurrentRunID != null && runID != mCurrentRunID) {
- throw new IllegalStateException(
- "Run #" + mCurrentRunID +
- " in progress. Cannot process run #" + runID);
+ public void onData(byte[] data, boolean eos, long runID) {
+ try {
+ String str = new String(data, "UTF-8");
+
+ // implement intermixing restriction for WebVTT only for now
+ synchronized(mParser) {
+ if (mCurrentRunID != null && runID != mCurrentRunID) {
+ throw new IllegalStateException(
+ "Run #" + mCurrentRunID +
+ " in progress. Cannot process run #" + runID);
+ }
+ mCurrentRunID = runID;
+ mParser.parse(str);
+ if (eos) {
+ finishedRun(runID);
+ mParser.eos();
+ mRegions.clear();
+ mCurrentRunID = null;
+ }
}
- mCurrentRunID = runID;
- mParser.parse(data);
- if (eos) {
- finishedRun(runID);
- mParser.eos();
- mRegions.clear();
- mCurrentRunID = null;
- }
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
}
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8eceee8..2e6b86e 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -133,7 +133,8 @@
ArrayList<MediaController> controllers = new ArrayList<MediaController>();
try {
List<IBinder> binders = mService.getSessions(notificationListener, userId);
- for (int i = binders.size() - 1; i >= 0; i--) {
+ int size = binders.size();
+ for (int i = 0; i < size; i++) {
MediaController controller = MediaController.fromBinder(ISessionController.Stub
.asInterface(binders.get(i)));
controllers.add(controller);
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index ec87c6e..2ed3d73 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -170,7 +170,7 @@
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packagePath, 0);
+ PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0);
if (pkg == null) {
Slog.w(TAG, "Failed to parse package");
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 2917faa..006b1ee 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -56,7 +56,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/date_owner_info_margin"
android:layout_gravity="center_horizontal"
- android:textColor="#99ffffff"
+ android:textColor="#ccffffff"
android:textSize="@dimen/widget_label_font_size"
android:ellipsize="marquee"
android:singleLine="true" />
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index e9e9b89..69bf44f 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -63,14 +63,11 @@
<dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
<!-- Overload default clock widget parameters -->
- <dimen name="widget_big_font_size">96dp</dimen>
+ <dimen name="widget_big_font_size">120dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="bottom_text_spacing_digital">-8dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-16dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">0dip</dimen>
-
- <!-- The vertical margin between the date and the owner info. -->
- <dimen name="date_owner_info_margin">4dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index f971522..4b113ff 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -155,15 +155,15 @@
<dimen name="eca_overlap">-10dip</dimen>
<!-- Default clock parameters -->
- <dimen name="bottom_text_spacing_digital">-6dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-10dp</dimen>
<dimen name="label_font_size">14dp</dimen>
- <dimen name="widget_label_font_size">14sp</dimen>
- <dimen name="widget_big_font_size">68dp</dimen>
+ <dimen name="widget_label_font_size">16sp</dimen>
+ <dimen name="widget_big_font_size">96dp</dimen>
<dimen name="big_font_size">120dp</dimen>
<!-- The y translation to apply at the start in appear animations. -->
<dimen name="appear_y_translation_start">32dp</dimen>
<!-- The vertical margin between the date and the owner info. -->
- <dimen name="date_owner_info_margin">2dp</dimen>
+ <dimen name="date_owner_info_margin">10dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/donottranslate.xml b/packages/Keyguard/res/values/donottranslate.xml
index 16f5a3e..78636db 100644
--- a/packages/Keyguard/res/values/donottranslate.xml
+++ b/packages/Keyguard/res/values/donottranslate.xml
@@ -16,7 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Skeleton string format for displaying the date. -->
- <string name="abbrev_wday_month_day_no_year">EEEMMMMd</string>
+ <string name="abbrev_wday_month_day_no_year">EEEEMMMMd</string>
<!-- Skeleton string format for displaying the time in 12-hour format. -->
<string name="clock_12hr_format">hm</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index a8b49e2..7918755 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -130,11 +130,6 @@
}
}
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
private void updateOwnerInfo() {
String ownerInfo = getOwnerInfo();
if (!TextUtils.isEmpty(ownerInfo)) {
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index bac8cb6..305a82f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="top">
+ android:zAdjustment="normal">
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index b0f8807f..863591f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="normal">
+ android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
index 2857c04..adcefe0 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
index 1139e72..863591f 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 9bf42b2..db5983b 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -46,7 +46,7 @@
android:id="@+id/keyguard_indication_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="100dp"
+ android:layout_marginBottom="70dp"
android:layout_gravity="bottom|center_horizontal"
android:textStyle="italic"
android:textColor="#ffffff"
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
new file mode 100644
index 0000000..f9b01c8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 326f602..5367fbc 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -23,5 +23,8 @@
<item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
<fraction name="keyguard_clock_y_fraction_max">37%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">14%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
+
+ <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 6dea81f..47581a9 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -28,4 +28,8 @@
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
+
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">5</integer>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 313e2e8..a5e3924 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -16,8 +16,8 @@
*/
-->
<resources>
- <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) -->
- <dimen name="notification_panel_width">478dp</dimen>
+ <!-- The width of the notification panel window: 400 + 16 + 16 (padding in the bg drawable) -->
+ <dimen name="notification_panel_width">432dp</dimen>
<!-- Gravity for the notification panel -->
<!-- 0x31 = top|center_horizontal -->
@@ -56,10 +56,10 @@
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
<fraction name="keyguard_clock_y_fraction_max">34%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">25%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
- <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_min">44dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_max">44dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5ffe3b3..7ed3e29 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -292,12 +292,12 @@
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">29.5%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">18%</fraction>
+ <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_min">24dp</dimen>
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
<dimen name="heads_up_window_height">250dp</dimen>
@@ -330,6 +330,4 @@
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
- <!-- the distance the panel moves up when starting the up motion on Keyguard -->
- <dimen name="keyguard_panel_move_up_distance">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 43560a3..c117eba 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -63,7 +63,7 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<!-- Note: must be dp to fit in status bar -->
<item name="android:textSize">16dp</item>
- <item name="android:textStyle">normal</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index e8f3745..c3ba349c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -126,7 +126,7 @@
}
private void updateNotification() {
- Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
if (mInvalidCharger) {
showInvalidChargerNotification();
@@ -238,13 +238,13 @@
@Override
public void dismissLowBatteryWarning() {
- Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
+ if (DEBUG) Slog.d(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
dismissLowBatteryNotification();
mFallbackDialogs.dismissLowBatteryWarning();
}
private void dismissLowBatteryNotification() {
- Slog.i(TAG, "dismissing low battery notification");
+ if (mWarning) Slog.i(TAG, "dismissing low battery notification");
mWarning = false;
updateNotification();
}
@@ -307,7 +307,7 @@
}
private void dismissInvalidChargerNotification() {
- Slog.i(TAG, "dismissing invalid charger notification");
+ if (mInvalidCharger) Slog.i(TAG, "dismissing invalid charger notification");
mInvalidCharger = false;
updateNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d66968b..dce8f57 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -117,7 +117,7 @@
// Otherwise, just finish the activity without launching any other activities
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -129,7 +129,7 @@
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -162,6 +162,13 @@
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+ ArrayList<TaskStack> stacks = root.getStacks();
+ if (!stacks.isEmpty()) {
+ mRecentsView.setBSP(root);
+ }
+
// Update the configuration based on the launch intent
mConfig.launchedFromHome = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
@@ -171,13 +178,7 @@
AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
-
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
- ArrayList<TaskStack> stacks = root.getStacks();
- if (!stacks.isEmpty()) {
- mRecentsView.setBSP(root);
- }
+ mConfig.launchedWithNoRecentTasks = !root.hasTasks();
if (mConfig.shouldAnimateNavBarScrim()) {
// Hide the scrim if we animate into Recents with window transitions
@@ -188,14 +189,12 @@
}
// Add the default no-recents layout
- if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
+ if (mConfig.launchedWithNoRecentTasks) {
mEmptyView.setVisibility(View.VISIBLE);
+ mEmptyView.setBackgroundColor(0x80000000);
} else {
mEmptyView.setVisibility(View.GONE);
}
-
- // Dim the background
- mRecentsView.setBackgroundColor(0x80000000);
}
/** Attempts to allocate and bind the search bar app widget */
@@ -284,7 +283,7 @@
// We really shouldn't hit this, but if we do, just animate out (aka. finish)
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -376,7 +375,7 @@
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -547,7 +546,7 @@
// Just start the animation out of recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
} else {
// Otherwise, try and launch the first task
@@ -555,7 +554,7 @@
// If there are no tasks, then just finish recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -567,12 +566,25 @@
// Fade in the scrim
if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
mNavBarScrimView.setVisibility(View.VISIBLE);
- mNavBarScrimView.setAlpha(0f);
- mNavBarScrimView.animate().alpha(1f)
+ mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+ mNavBarScrimView.animate()
+ .translationY(0)
.setStartDelay(mConfig.taskBarEnterAnimDelay)
.setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .start();
+ }
+ }
+
+ @Override
+ public void onExitAnimationTriggered() {
+ // Fade out the scrim
+ if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
+ mNavBarScrimView.animate()
+ .translationY(mNavBarScrimView.getMeasuredHeight())
+ .setStartDelay(0)
+ .setDuration(mConfig.taskBarExitAnimDuration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
.start();
}
}
@@ -598,12 +610,7 @@
// Fade out the scrim
if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
- mNavBarScrimView.animate().alpha(0f)
- .setStartDelay(0)
- .setDuration(mConfig.taskBarExitAnimDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
- .start();
+ onExitAnimationTriggered();
}
// Mark recents as no longer visible
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3b8c6c9..c1a8ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -97,6 +97,7 @@
/** Launch states */
public boolean launchedWithAltTab;
+ public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromAppWithScreenshot;
public boolean launchedFromHome;
@@ -245,6 +246,7 @@
* members. */
public void updateOnConfigurationChange() {
launchedWithAltTab = false;
+ launchedWithNoRecentTasks = false;
launchedFromAppWithThumbnail = false;
launchedFromAppWithScreenshot = false;
launchedFromHome = false;
@@ -257,12 +259,14 @@
/** Returns whether the nav bar scrim should be animated when shown for the first time. */
public boolean shouldAnimateNavBarScrim() {
- return !launchedFromHome && !launchedFromAppWithScreenshot;
+ return true;
}
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
- return !transposeRecentsLayoutWithOrientation || !isLandscape;
+ // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+ return !launchedWithNoRecentTasks &&
+ (!transposeRecentsLayoutWithOrientation || !isLandscape);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index dbcdb94..a02e1a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -394,7 +394,7 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier());
+ ssp.getRecentTasks(100, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
if (Console.Enabled) {
Console.log(Constants.Log.App.TimeSystemCalls,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
index 1dd1be6..20be415 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
@@ -56,6 +56,13 @@
return mStack;
}
+ /** Returns whether there are any tasks in any stacks below this node. */
+ public boolean hasTasks() {
+ return (mStack.getTaskCount() > 0) ||
+ (mStartNode != null && mStartNode.hasTasks()) ||
+ (mEndNode != null && mEndNode.hasTasks());
+ }
+
/** Returns whether this is a leaf node */
boolean isLeafNode() {
return (mStartNode == null) && (mEndNode == null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
index ad2fa8d..c861d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -99,12 +99,17 @@
@Override
public void draw(Canvas canvas) {
- int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
super.draw(canvas);
canvas.restoreToCount(restoreCount);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Prepares the screenshot view for the transition into Recents */
public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
if (!mConfig.launchedFromAppWithScreenshot) return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6f79683..3e6879d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -55,6 +55,7 @@
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskLaunching(boolean isTaskInStackBounds);
+ public void onExitAnimationTriggered();
}
RecentsConfiguration mConfig;
@@ -160,19 +161,19 @@
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation(ctx);
+ stackView.startEnterRecentsAnimation(ctx);
}
}
}
/** Requests all task stacks to start their exit-recents animation */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.increment();
@@ -183,7 +184,7 @@
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnExitAnimation(ctx);
+ stackView.startExitToHomeAnimation(ctx);
}
}
}
@@ -191,6 +192,9 @@
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.decrement();
+
+ // Notify of the exit animation
+ mCb.onExitAnimationTriggered();
}
/** Adds the search bar */
@@ -476,7 +480,7 @@
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLaunchingTask(launchRunnable);
+ stackView.animateOnLaunchingTask(tv, launchRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index d4f381b..82d6220 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -124,6 +124,11 @@
}
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
mTask = t;
@@ -158,12 +163,12 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents() {
+ public void prepareEnterRecentsAnimation() {
setVisibility(View.INVISIBLE);
}
/** Animates this task bar as it enters recents */
- public void animateOnEnterRecents(int delay, Runnable postAnimRunnable) {
+ public void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
// Animate the task bar of the first task view
setVisibility(View.VISIBLE);
setTranslationY(-getMeasuredHeight());
@@ -177,7 +182,7 @@
}
/** Animates this task bar as it exits recents */
- public void animateOnLaunchingTask(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
+ public void startLaunchTaskAnimation(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
// Animate the task bar out of the first task view
animate()
.translationY(-getMeasuredHeight())
@@ -194,8 +199,22 @@
.start();
}
+ /** Animates this task bar dismiss button when launching a task. */
+ public void startLaunchTaskDismissAnimation() {
+ if (mDismissButton.getVisibility() == View.VISIBLE) {
+ mDismissButton.animate().cancel();
+ mDismissButton.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
/** Animates this task bar if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
+ public void startNoUserInteractionAnimation() {
mDismissButton.setVisibility(View.VISIBLE);
mDismissButton.setAlpha(0f);
mDismissButton.animate()
@@ -208,7 +227,7 @@
}
/** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
+ public void setNoUserInteractionState() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.animate().cancel();
mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 79bfa5e..e0a12b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -116,7 +116,7 @@
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnNoUserInteraction();
+ tv.startNoUserInteractionAnimation();
}
}
});
@@ -532,6 +532,20 @@
}
}
+ /** Animates a task view in this stack as it launches. */
+ public void animateOnLaunchingTask(TaskView tv, final Runnable r) {
+ // Hide each of the task bar dismiss buttons
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView t = (TaskView) getChildAt(i);
+ if (t == tv) {
+ t.startLaunchTaskAnimation(r, true);
+ } else {
+ t.startLaunchTaskAnimation(null, false);
+ }
+ }
+ }
+
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (Console.Enabled) {
@@ -818,14 +832,14 @@
int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
- tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ tv.prepareEnterRecentsAnimation((i == (getChildCount() - 1)), offsetTopAlign,
offscreenY, mTaskRect);
}
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation(mStartEnterAnimationContext);
+ startEnterRecentsAnimation(mStartEnterAnimationContext);
mStartEnterAnimationRequestedAfterLayout = false;
mStartEnterAnimationContext = null;
}
@@ -838,7 +852,7 @@
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
@@ -858,18 +872,18 @@
ctx.stackViewCount = childCount;
ctx.isFrontMost = (i == (getChildCount() - 1));
ctx.transform = transform;
- tv.animateOnEnterRecents(ctx);
+ tv.startEnterRecentsAnimation(ctx);
}
}
/** Requests this task stacks to start it's exit-recents animation. */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Animate all the task views into view
ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnExitRecents(ctx);
+ tv.startExitToHomeAnimation(ctx);
}
}
@@ -1179,7 +1193,7 @@
// If the doze trigger has already fired, then update the state for this task view
if (mDozeTrigger.hasTriggered()) {
- tv.setOnNoUserInteraction();
+ tv.setNoUserInteractionState();
}
// Add/attach the view to the hierarchy
@@ -1275,7 +1289,7 @@
TaskView tv = getChildViewForTask(t);
if (tv != null) {
// For visible children, defer removing the task until after the animation
- tv.animateRemoval(new Runnable() {
+ tv.startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
mStack.removeTask(t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index dc8a420..c2b2094 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -54,10 +54,13 @@
@Override
public void draw(Canvas canvas) {
if (mClipTaskBar && (mClipRect != null)) {
- // Apply the clip rect
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ } else {
+ super.draw(canvas);
}
- super.draw(canvas);
}
/** Updates the clip rect based on the given task bar. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b7e834b..09dc1c8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -259,15 +259,15 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
- Rect taskRect) {
+ public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
if (mConfig.launchedFromAppWithScreenshot) {
if (isTaskViewFrontMost) {
// Hide the task view as we are going to animate the full screenshot into view
// and then replace it with this view once we are done
setVisibility(View.INVISIBLE);
// Also hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
} else {
// Top align the task views
setTranslationY(offsetY);
@@ -278,7 +278,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (isTaskViewFrontMost) {
// Hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
// Set the dim to 0 so we can animate it in
setDim(0);
}
@@ -286,13 +286,14 @@
} else if (mConfig.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
+ setTranslationZ(0);
setScaleX(1f);
setScaleY(1f);
}
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
TaskViewTransform transform = ctx.transform;
if (mConfig.launchedFromAppWithScreenshot) {
@@ -302,7 +303,7 @@
@Override
public void run() {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(0, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
setVisibility(View.VISIBLE);
}
});
@@ -324,7 +325,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (ctx.isFrontMost) {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
// Animate the dim into view as well
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
@@ -345,6 +346,7 @@
.scaleX(transform.scale)
.scaleY(transform.scale)
.translationY(transform.translationY)
+ .translationZ(transform.translationZ)
.setStartDelay(delay)
.setUpdateListener(null)
.setInterpolator(mConfig.quintOutInterpolator)
@@ -355,8 +357,8 @@
}
}
- /** Animates this task view as it leaves recents */
- public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ /** Animates this task view as it leaves recents by pressing home. */
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
@@ -369,32 +371,27 @@
ctx.postAnimationTrigger.increment();
}
- /** Animates this task view if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
- mBarView.animateOnNoUserInteraction();
- }
-
- /** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
- mBarView.setOnNoUserInteraction();
- }
-
/** Animates this task view as it exits recents */
- public void animateOnLaunchingTask(final Runnable r) {
- // Disable the thumbnail clip and animate the bar out
- mBarView.animateOnLaunchingTask(mDisableThumbnailClip, r);
+ public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
+ if (isLaunchingTask) {
+ // Disable the thumbnail clip and animate the bar out
+ mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
- // Animate the dim
- if (mDim > 0) {
- ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
- anim.setDuration(mConfig.taskBarExitAnimDuration);
- anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
- anim.start();
+ // Animate the dim
+ if (mDim > 0) {
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
+ anim.setDuration(mConfig.taskBarExitAnimDuration);
+ anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.start();
+ }
+ } else {
+ // Hide the dismiss button
+ mBarView.startLaunchTaskDismissAnimation();
}
}
/** Animates the deletion of this task view */
- public void animateRemoval(final Runnable r) {
+ public void startDeleteTaskAnimation(final Runnable r) {
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
@@ -422,6 +419,16 @@
.start();
}
+ /** Animates this task view if the user does not interact with the stack after a certain time. */
+ public void startNoUserInteractionAnimation() {
+ mBarView.startNoUserInteractionAnimation();
+ }
+
+ /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ public void setNoUserInteractionState() {
+ mBarView.setNoUserInteractionState();
+ }
+
/** Returns the rect we want to clip (it may not be the full rect) */
Rect getClippingRect(Rect outRect) {
getHitRect(outRect);
@@ -489,10 +496,11 @@
@Override
public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
// Apply the rounded rect clip path on the whole view
canvas.clipPath(mRoundedRectClipPath);
-
super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
// Apply the dim if necessary
if (mDim > 0) {
@@ -581,7 +589,7 @@
} else if (v == mBarView.mDismissButton) {
// Animate out the view and call the callback
final TaskView tv = this;
- animateRemoval(new Runnable() {
+ startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
mCb.onTaskDismissed(tv);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index db85b14..6a83a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -88,15 +88,13 @@
public void run(Result result) {
int y = getClockY() - mKeyguardStatusHeight/2;
- float topAdjustment = getTopExpansionAdjustment();
+ float clockAdjustment = getClockYExpansionAdjustment();
float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
- result.stackScrollerPaddingAdjustment = (int) (topAdjustment*topPaddingAdjMultiplier);
+ result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
int clockNotificationsPadding = getClockNotificationsPadding()
+ result.stackScrollerPaddingAdjustment;
int padding = y + clockNotificationsPadding;
- if (mNotificationCount == 0) {
- y += topAdjustment;
- }
+ y += clockAdjustment;
result.clockY = y;
result.stackScrollerPadding = mKeyguardStatusHeight + padding;
result.clockAlpha = getClockAlpha(result.stackScrollerPadding
@@ -119,8 +117,8 @@
return (int) (getClockYFraction() * mHeight);
}
- private float getTopExpansionAdjustment() {
- float rubberbandFactor = getTopExpansionRubberbandFactor();
+ private float getClockYExpansionAdjustment() {
+ float rubberbandFactor = getClockYExpansionRubberbandFactor();
float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
float t = value / mMaxPanelHeight;
float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
@@ -132,7 +130,7 @@
}
}
- private float getTopExpansionRubberbandFactor() {
+ private float getClockYExpansionRubberbandFactor() {
float t = getNotificationAmountT();
t = Math.min(t, 1.0f);
t = (float) Math.pow(t, 0.3f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
index ca49408..e312d58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -246,9 +246,7 @@
private void startHintTranslationAnimations(float target, long duration,
Interpolator interpolator) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
- int length = targetViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = targetViews.get(i);
+ for (View targetView : targetViews) {
targetView.animate()
.setDuration(duration)
.setInterpolator(interpolator)
@@ -261,16 +259,8 @@
}
private void cancelAnimations() {
- ArrayList<View> translatingViews = mCallback.getTranslationViews();
- int length = translatingViews.size();
- for (int i = 0; i < length; i++) {
- View target = translatingViews.get(i);
- target.animate().cancel();
- }
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View target = fadingViews.get(i);
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ for (View target : targetViews) {
target.animate().cancel();
}
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
@@ -301,11 +291,6 @@
// translation Animation
startTranslationAnimations(vel, target);
- // fade animations
- if (snapBack) {
- startFadeInAnimations();
- }
-
// animate left / right icon
startIconAnimation(vel, snapBack, target);
@@ -361,20 +346,9 @@
mSwipeAnimator = animator;
}
- private void startFadeInAnimations() {
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- int length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = fadingViews.get(i);
- targetView.animate().alpha(1.0f);
- }
- }
-
private void startTranslationAnimations(float vel, float target) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
- int length = targetViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = targetViews.get(i);
+ for (View targetView : targetViews) {
ViewPropertyAnimator animator = targetView.animate();
mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
animator.translationX(target);
@@ -401,18 +375,8 @@
translation = leftSwipePossible() ? translation : Math.min(0, translation);
if (translation != mTranslation || isReset) {
ArrayList<View> translatedViews = mCallback.getTranslationViews();
- int length = translatedViews.size();
- for (int i = 0; i < length; i++) {
- View target = translatedViews.get(i);
- target.setTranslationX(translation);
- }
- float targetAlpha = 1.0f - Math.abs(translation / mMinTranslationAmount);
- targetAlpha = Math.max(0.0f, targetAlpha);
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View view = fadingViews.get(i);
- view.setAlpha(targetAlpha);
+ for (View view : translatedViews) {
+ view.setTranslationX(translation);
}
if (translation == 0.0f) {
boolean animate = !isReset;
@@ -530,8 +494,6 @@
ArrayList<View> getTranslationViews();
- ArrayList<View> getFadingViews();
-
View getLeftIcon();
View getCenterIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5c686fc..dde95bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -108,7 +108,6 @@
private KeyguardBottomAreaView mKeyguardBottomArea;
private boolean mBlockTouches;
private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
- private ArrayList<View> mSwipeFadingViews = new ArrayList<>();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -153,7 +152,7 @@
android.R.interpolator.fast_out_linear_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mSwipeTranslationViews.add(mNotificationStackScroller);
- mSwipeFadingViews.add(mKeyguardStatusView);
+ mSwipeTranslationViews.add(mKeyguardStatusView);
mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
}
@@ -689,6 +688,9 @@
@Override
protected boolean isScrolledToBottom() {
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ return true;
+ }
if (!isInSettings()) {
return mNotificationStackScroller.isScrolledToBottom();
}
@@ -696,11 +698,6 @@
}
@Override
- protected boolean hasNotifications() {
- return mNotificationStackScroller.getNotGoneChildCount() > 0;
- }
-
- @Override
protected int getMaxPanelHeight() {
// TODO: Figure out transition for collapsing when QS is open, adjust height here.
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
@@ -964,11 +961,6 @@
}
@Override
- public ArrayList<View> getFadingViews() {
- return mSwipeFadingViews;
- }
-
- @Override
public View getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getCameraImageView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 08305dc..1f3098d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -30,8 +30,6 @@
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
@@ -45,8 +43,6 @@
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
- private static final long KEYGUARD_MOVE_UP_LENGTH = 300;
-
private final void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
@@ -66,9 +62,6 @@
protected int mTouchSlop;
protected boolean mHintAnimationRunning;
private boolean mOverExpandedBeforeFling;
- private boolean mKeyguardMovingUp;
- private int mKeyguardMoveUpDistance;
- private float mKeyguardFingerHeight;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -89,8 +82,6 @@
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
- private Interpolator mKeyguardMoveUpInterpolator;
- private final Interpolator mLinearInterpolator = new LinearInterpolator();
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -118,7 +109,6 @@
mLinearOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
- mKeyguardMoveUpInterpolator = new PathInterpolator(0.6f, 0f, 0.4f, 1f);
}
protected void loadDimens() {
@@ -130,8 +120,6 @@
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
- mKeyguardMoveUpDistance =
- res.getDimensionPixelSize(R.dimen.keyguard_panel_move_up_distance);
}
private void trackMovement(MotionEvent event) {
@@ -229,13 +217,8 @@
mJustPeeked = false;
}
if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
- if (mStatusBar.getBarState() == StatusBarState.KEYGUARD &&
- !hasNotifications()) {
- setExpandedHeightKeyguard(newHeight);
- } else {
- setExpandedHeightInternal(newHeight);
- mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
- }
+ setExpandedHeightInternal(newHeight);
+ mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
}
trackMovement(event);
@@ -264,56 +247,10 @@
return !waitForTouchSlop || mTracking;
}
- protected abstract boolean hasNotifications();
-
- private void setExpandedHeightKeyguard(float newHeight) {
- mKeyguardFingerHeight = newHeight;
- if (newHeight < getMaxPanelHeight() && !mKeyguardMovingUp) {
- mKeyguardMovingUp = true;
- ValueAnimator anim = createHeightAnimator(
- getMaxPanelHeight() - mKeyguardMoveUpDistance);
- anim.setDuration(KEYGUARD_MOVE_UP_LENGTH);
- anim.setInterpolator(mKeyguardMoveUpInterpolator);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeightAnimator = null;
- }
- });
- mHeightAnimator = anim;
- anim.start();
- postOnAnimationDelayed(new Runnable() {
- @Override
- public void run() {
- if (mKeyguardFingerHeight < mExpandedHeight && mHeightAnimator != null
- && mKeyguardMovingUp) {
- mHeightAnimator.cancel();
- float target = getMaxPanelHeight() - 1.75f * mKeyguardMoveUpDistance;
- float diff = mExpandedHeight - target;
- ValueAnimator anim = createHeightAnimator(target);
- float velocity = 2.5f * mKeyguardMoveUpDistance /
- (KEYGUARD_MOVE_UP_LENGTH / 1000f);
- anim.setInterpolator(mLinearInterpolator);
- anim.setDuration(Math.max(0, (long) (diff / velocity * 1000f)));
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeightAnimator = null;
- }
- });
- mHeightAnimator = anim;
- anim.start();
- }
- }
- }, KEYGUARD_MOVE_UP_LENGTH / 2);
- }
- }
-
protected abstract boolean hasConflictingGestures();
protected void onTrackingStopped(boolean expand) {
mTracking = false;
- mKeyguardMovingUp = false;
mBar.onTrackingStopped(PanelView.this, expand);
}
@@ -444,9 +381,6 @@
protected void fling(float vel, boolean expand) {
cancelPeek();
- if (mHeightAnimator != null) {
- mHeightAnimator.cancel();
- }
float target = expand ? getMaxPanelHeight() : 0.0f;
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
onExpandingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b23992d..1da7dab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2532,6 +2532,7 @@
repositionNavigationBar();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
updateShowSearchHoldoff();
+ updateRowStates();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 94472a3..4cbb06b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -241,11 +241,12 @@
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
+ mStackScrollAlgorithm.setDimmed(mAmbientState.isDimmed());
mPaddingBetweenElementsDimmed = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding_dimmed);
mPaddingBetweenElementsNormal = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding);
- updatePadding(false);
+ updatePadding(mAmbientState.isDimmed());
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this,
@@ -761,11 +762,12 @@
}
}
}
-
- mActivePointerId = INVALID_POINTER;
- endDrag();
}
+
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
}
+
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index cbad9dc..602c22b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -587,6 +587,7 @@
algorithmState.itemsInTopStack += algorithmState.partialInTop;
newSize = Math.max(mCollapsedSize, newSize);
if (i == 0) {
+ algorithmState.itemsInTopStack = 1.0f;
childViewState.height = (int) newSize;
}
algorithmState.lastTopStackIndex = i;
@@ -617,6 +618,20 @@
if (i < algorithmState.itemsInTopStack) {
float stackIndex = algorithmState.itemsInTopStack - i;
stackIndex = Math.min(stackIndex, MAX_ITEMS_IN_TOP_STACK + 2);
+ if (i == 0 && algorithmState.itemsInTopStack < 2.0f) {
+
+ // We only have the top item and an additional item in the top stack,
+ // Interpolate the index from 0 to 2 while the second item is
+ // translating in.
+ stackIndex -= 1.0f;
+ if (algorithmState.scrollY > mCollapsedSize) {
+
+ // Since there is a shadow treshhold, we cant just interpolate from 0 to
+ // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in
+ // height will not be noticable since we have padding in between.
+ stackIndex = 0.1f + stackIndex * 1.9f;
+ }
+ }
childViewState.zTranslation = mZBasicHeight
+ stackIndex * mZDistanceBetweenElements;
} else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index b2ecb616..4ad45a8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -40,9 +40,6 @@
private static String TAG = "PhoneFallbackEventHandler";
private static final boolean DEBUG = false;
- // Use the new sessions APIs
- private static final boolean USE_SESSIONS = true;
-
Context mContext;
View mView;
@@ -294,21 +291,7 @@
}
private void handleMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
- } else {
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.checkService(Context.AUDIO_SERVICE));
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
- }
- } else {
- Slog.w(TAG, "Unable to find IAudioService for media key event.");
- }
- }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 5598972..bc2671011 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -144,9 +144,6 @@
static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true;
static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
- // Whether to use the new Session APIs
- static final boolean USE_SESSIONS = true;
-
static final int SHORT_PRESS_POWER_NOTHING = 0;
static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
@@ -3974,6 +3971,7 @@
* controlled by this device, or through remote submix).
*/
boolean isMusicActive() {
+
final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
if (am == null) {
Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
@@ -3982,43 +3980,6 @@
return am.isLocalOrRemoteMusicActive();
}
- /**
- * Tell the audio service to adjust the volume appropriate to the event.
- * @param keycode
- */
- void handleVolumeKey(int stream, int keycode) {
- IAudioService audioService = getAudioService();
- if (audioService == null) {
- return;
- }
- try {
- // when audio is playing locally, we shouldn't have to hold a wake lock
- // during the call, but we do it as a precaution for the rare possibility
- // that the music stops right before we call this.
- // Otherwise we might also be in a remote playback case.
- // TODO: Actually handle MUTE.
- mBroadcastWakeLock.acquire();
- if (stream == AudioSystem.STREAM_MUSIC) {
- audioService.adjustLocalOrRemoteStreamVolume(stream,
- keycode == KeyEvent.KEYCODE_VOLUME_UP
- ? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER,
- mContext.getOpPackageName());
- } else {
- audioService.adjustStreamVolume(stream,
- keycode == KeyEvent.KEYCODE_VOLUME_UP
- ? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER,
- 0,
- mContext.getOpPackageName());
- }
- } catch (RemoteException e) {
- Log.w(TAG, "IAudioService.adjust*StreamVolume() threw RemoteException " + e);
- } finally {
- mBroadcastWakeLock.release();
- }
- }
-
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
@@ -4201,16 +4162,20 @@
if (telephonyManager.isOffhook()
&& (result & ACTION_PASS_TO_USER) == 0) {
// If we are in call but we decided not to pass the key to
- // the application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+ // the application, just pass it to the session service.
+
+ MediaSessionLegacyHelper.getHelper(mContext)
+ .sendMediaButtonEvent(event, true);
break;
}
}
- if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
- // If music is playing but we decided not to pass the key to the
- // application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
+ if ((result & ACTION_PASS_TO_USER) == 0) {
+ // If we aren't passing to the user and no one else
+ // handled it send it to the session manager to figure
+ // out.
+ MediaSessionLegacyHelper.getHelper(mContext)
+ .sendMediaButtonEvent(event, true);
break;
}
}
@@ -4459,18 +4424,7 @@
void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
if (ActivityManagerNative.isSystemReady()) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
- } else {
- IAudioService audioService = getAudioService();
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEventUnderWakelock(event);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
- }
- }
- }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index e2a8ca2..07c9048 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -308,6 +308,10 @@
}
public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
+ if (callback == null) {
+ Log.w(TAG, "Callback is null in registerAdapter");
+ return null;
+ }
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
@@ -317,6 +321,10 @@
}
public void unregisterAdapter(IBluetoothManagerCallback callback) {
+ if (callback == null) {
+ Log.w(TAG, "Callback is null in unregisterAdapter");
+ return;
+ }
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index bcd08ebf..23cf40b 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -26,31 +26,29 @@
/**
* Handles CEC command <Active Source>.
- *
- * <p>Used by feature actions that need to handle the command in their flow.
+ * <p>
+ * Used by feature actions that need to handle the command in their flow.
*/
final class ActiveSourceHandler {
private static final String TAG = "ActiveSourceHandler";
+ private final HdmiCecLocalDevice mSource;
private final HdmiControlService mService;
- private final int mSourceAddress;
- private final int mSourcePath;
- @Nullable private final IHdmiControlCallback mCallback;
+ @Nullable
+ private final IHdmiControlCallback mCallback;
- static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
- int sourcePath, IHdmiControlCallback callback) {
- if (service == null) {
+ static ActiveSourceHandler create(HdmiCecLocalDevice source,
+ IHdmiControlCallback callback) {
+ if (source == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ return new ActiveSourceHandler(source, callback);
}
- private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
- IHdmiControlCallback callback) {
- mService = service;
- mSourceAddress = sourceAddress;
- mSourcePath = sourcePath;
+ private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ mSource = source;
+ mService = mSource.getService();
mCallback = callback;
}
@@ -61,7 +59,7 @@
* @param routingPath routing path of the device to be the active source
*/
void process(int deviceLogicalAddress, int routingPath) {
- if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
invokeCallback(HdmiCec.RESULT_SUCCESS);
return;
}
@@ -69,14 +67,14 @@
if (device == null) {
// "New device action" initiated by <Active Source> does not require
// "Routing change action".
- mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
- deviceLogicalAddress, routingPath, false));
+ mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
+ routingPath, false));
}
- if (!mService.isInPresetInstallationMode()) {
- int prevActiveInput = mService.getActiveInput();
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
- if (prevActiveInput != mService.getActiveInput()) {
+ if (!mSource.isInPresetInstallationMode()) {
+ int prevActiveInput = mSource.getActiveInput();
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mSource.getActiveInput()) {
// TODO: change port input here.
}
invokeCallback(HdmiCec.RESULT_SUCCESS);
@@ -84,24 +82,33 @@
// TV is in a mode that should keep its current source/input from
// being changed for its operation. Reclaim the active source
// or switch the port back to the one used for the current mode.
- if (mService.getActiveSource() == mSourceAddress) {
+ if (mSource.getActiveSource() == getSourceAddress()) {
HdmiCecMessage activeSource =
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
+ getSourcePath());
mService.sendCecCommand(activeSource);
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
- int activePath = mService.getActivePath();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ int activePath = mSource.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
routingPath, activePath));
// TODO: Start port select action here
- // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
- // activePath, mCallback);
+ // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
+ // activePath, mCallback);
// mService.addActionAndStart(action);
}
}
}
+ private final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ private final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
+ }
+
private void invokeCallback(int result) {
if (mCallback == null) {
return;
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index f7392e9..daeff3c 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -94,12 +94,10 @@
/**
* Constructor.
*
- * @param service an instance of {@link HdmiControlService}.
- * @param sourceAddress a logical address which initiates this action
+ * @param source an instance of {@link HdmiCecLocalDevice}.
*/
- DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
- DeviceDiscoveryCallback callback) {
- super(service, sourceAddress);
+ DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
+ super(source);
mCallback = Preconditions.checkNotNull(callback);
}
@@ -108,7 +106,7 @@
mDevices.clear();
mState = STATE_WAITING_FOR_DEVICE_POLLING;
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
if (ackedAddress.isEmpty()) {
@@ -156,7 +154,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -179,7 +177,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -203,12 +201,13 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+ sendCommand(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
private boolean mayProcessMessageIfCached(int address, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
if (message != null) {
processCommand(message);
return true;
diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
index 63c2182..51df473 100644
--- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
@@ -16,9 +16,10 @@
* limitations under the License.
*/
-import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -40,18 +41,18 @@
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
- static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
+ static DevicePowerStatusAction create(HdmiCecLocalDevice source,
int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
+ return new DevicePowerStatusAction(source, targetAddress, callback);
}
- private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
+ private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@@ -65,8 +66,8 @@
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index a8696a1..dbe3b80 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -16,10 +16,11 @@
package com.android.server.hdmi;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -66,24 +67,20 @@
private final HdmiCecDeviceInfo mTarget;
private final IHdmiControlCallback mCallback;
- private final int mSourcePath;
private int mPowerStatusCounter = 0;
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of TV initiating this action
- * @param sourcePath physical address of TV
+ * @param source {@link HdmiCecLocalDevice} instance
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ public DeviceSelectAction(HdmiCecLocalDevice source,
HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(source);
mCallback = callback;
- mSourcePath = sourcePath;
mTarget = target;
}
@@ -96,7 +93,7 @@
private void queryDevicePowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mSourceAddress, mTarget.getLogicalAddress()));
+ getSourceAddress(), mTarget.getLogicalAddress()));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_MS);
}
@@ -118,7 +115,8 @@
case STATE_WAIT_FOR_ACTIVE_SOURCE:
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
int activePath = HdmiUtils.twoBytesToInt(params);
- ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+ ActiveSourceHandler
+ .create(localDevice(), mCallback)
.process(cmd.getSource(), activePath);
finish();
return true;
@@ -174,15 +172,15 @@
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
- mSourceAddress, mTarget.getPhysicalAddress()));
+ getSourceAddress(), mTarget.getPhysicalAddress()));
mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
}
private void sendRemoteKeyCommand(int keyCode) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
mTarget.getLogicalAddress(), keyCode));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTarget.getLogicalAddress()));
}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index ae272b4..0ec17f6 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -22,6 +22,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.List;
/**
* Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
@@ -33,14 +36,13 @@
* of the object. All the actual action classes inherit FeatureAction.
*
* <p>More than one FeatureAction objects can be up and running simultaneously,
- * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
* arriving from the bus, and either consumes it if the command is what the action expects,
* or yields it to other action.
*
* Declared as package private, accessed by {@link HdmiControlService} only.
*/
abstract class FeatureAction {
-
private static final String TAG = "FeatureAction";
// Timer handler message used for timeout event
@@ -56,19 +58,16 @@
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
- protected final HdmiControlService mService;
-
- // Logical address of the device for which the feature action is taken. The commands
- // generated in an action all use this field as source address.
- protected final int mSourceAddress;
+ private final HdmiControlService mService;
+ private final HdmiCecLocalDevice mSource;
// Timer that manages timeout events.
protected ActionTimer mActionTimer;
- FeatureAction(HdmiControlService service, int sourceAddress) {
- mService = service;
- mSourceAddress = sourceAddress;
- mActionTimer = createActionTimer(service.getServiceLooper());
+ FeatureAction(HdmiCecLocalDevice source) {
+ mSource = source;
+ mService = mSource.getService();
+ mActionTimer = createActionTimer(mService.getServiceLooper());
}
@VisibleForTesting
@@ -175,6 +174,42 @@
mService.sendCecCommand(cmd, callback);
}
+ protected final void addAndStartAction(FeatureAction action) {
+ mSource.addAndStartAction(action);
+ }
+
+ protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ return mSource.getActions(clazz);
+ }
+
+ protected final HdmiCecMessageCache getCecMessageCache() {
+ return mSource.getCecMessageCache();
+ }
+
+ /**
+ * Remove the action from the action queue. This is called after the action finishes
+ * its role.
+ *
+ * @param action
+ */
+ protected final void removeAction(FeatureAction action) {
+ mSource.removeAction(action);
+ }
+
+ protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ mSource.removeActionExcept(clazz, null);
+ }
+
+ protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ mSource.removeActionExcept(clazz, exception);
+ }
+
+ protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
+ int retryCount) {
+ mService.pollDevices(callback, pickStrategy, retryCount);
+ }
+
/**
* Clean up action's state.
*
@@ -194,13 +229,23 @@
removeAction(this);
}
- /**
- * Remove the action from the action queue. This is called after the action finishes
- * its role.
- *
- * @param action
- */
- private void removeAction(FeatureAction action) {
- mService.removeAction(action);
+ protected final HdmiCecLocalDevice localDevice() {
+ return mSource;
+ }
+
+ protected final HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mSource;
+ }
+
+ protected final HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mSource;
+ }
+
+ protected final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ protected final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index a0c635d..fe16869 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -17,7 +17,6 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
@@ -109,10 +108,6 @@
private HdmiControlService mService;
- // Map-like container of all cec devices including local ones.
- // A logical address of device is used as key of container.
- private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
-
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
@@ -224,90 +219,6 @@
return body;
}
- /**
- * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
- * that has the same logical address as new one has.
- */
- HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getLogicalAddress());
- }
- mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiCecDeviceInfo} if exists.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address of device to be removed
- * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
- if (deviceInfo != null) {
- mDeviceInfos.remove(logicalAddress);
- }
- return deviceInfo;
- }
-
- /**
- * Clear all device info.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- void clearDeviceInfoList() {
- assertRunOnServiceThread();
- mDeviceInfos.clear();
- }
-
- /**
- * Return a list of all {@link HdmiCecDeviceInfo}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param includeLocalDevice whether to add local device or not
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- if (includeLocalDevice) {
- return sparseArrayToList(mDeviceInfos);
- } else {
- ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
- if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
- infoList.add(info);
- }
- }
- return infoList;
- }
- }
-
- /**
- * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address to be retrieved
- * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(logicalAddress);
- }
HdmiPortInfo[] getPortInfos() {
return nativeGetPortInfos(mNativePtr);
@@ -451,7 +362,7 @@
*/
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
- return sparseArrayToList(mLocalDevices);
+ return HdmiUtils.sparseArrayToList(mLocalDevices);
}
private List<Integer> pickPollCandidates(int pickStrategy) {
@@ -489,14 +400,6 @@
return pollingCandidates;
}
- private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
- ArrayList<T> list = new ArrayList<>();
- for (int i = 0; i < array.size(); ++i) {
- list.add(array.valueAt(i));
- }
- return list;
- }
-
private boolean isAllocatedLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7a2a6cc..08d7786 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -19,8 +19,16 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Looper;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
@@ -34,10 +42,37 @@
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ private int mActiveSource;
+
+ // Active routing path. Physical address of the active source but not all the time, such as
+ // when the new active source does not claim itself to be one. Note that we don't keep
+ // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
+ @GuardedBy("mLock")
+ private int mActiveRoutingPath;
+
+ // Set to true while the service is in normal mode. While set to false, no input change is
+ // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+ // system upgrade, etc., a.k.a. "prohibit mode".
+ @GuardedBy("mLock")
+ private boolean mInputChangeEnabled;
+
+ protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+ protected final Object mLock;
+
+ // A collection of FeatureAction.
+ // Note that access to this collection should happen in service thread.
+ private final LinkedList<FeatureAction> mActions = new LinkedList<>();
+
protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
mService = service;
mDeviceType = deviceType;
mAddress = HdmiCec.ADDR_UNREGISTERED;
+ mLock = service.getServiceLock();
+
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
@@ -69,14 +104,23 @@
* @return true if consumed a message; otherwise, return false.
*/
final boolean dispatchMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int dest = message.getDestination();
if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
return false;
}
+ // Cache incoming message. Note that it caches only white-listed one.
+ mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
protected final boolean onMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
switch (message.getOpcode()) {
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
@@ -90,12 +134,31 @@
return handleGetCecVersion(message);
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
return handleReportPhysicalAddress(message);
+ case HdmiCec.MESSAGE_INITIATE_ARC:
+ return handleInitiateArc(message);
+ case HdmiCec.MESSAGE_TERMINATE_ARC:
+ return handleTerminateArc(message);
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ return handleSetSystemAudioMode(message);
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ return handleSystemAudioModeStatus(message);
default:
return false;
}
}
+ private boolean dispatchMessageToAction(HdmiCecMessage message) {
+ for (FeatureAction action : mActions) {
+ if (action.processCommand(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean handleGivePhysicalAddress() {
+ assertRunOnServiceThread();
+
int physicalAddress = mService.getPhysicalAddress();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, physicalAddress, mDeviceType);
@@ -104,6 +167,8 @@
}
protected boolean handleGiveDeviceVendorId() {
+ assertRunOnServiceThread();
+
int vendorId = mService.getVendorId();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, vendorId);
@@ -112,6 +177,8 @@
}
protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int version = mService.getCecVersion();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
message.getSource(), version);
@@ -120,6 +187,8 @@
}
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
mService.sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
@@ -129,6 +198,8 @@
}
protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
// Note that since this method is called after logical address allocation is done,
// mDeviceInfo should not be null.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
@@ -149,34 +220,222 @@
return false;
}
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ return false;
+ }
+
final void handleAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
+
mAddress = mPreferredAddress = logicalAddress;
onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
+ assertRunOnServiceThread();
return mDeviceInfo;
}
void setDeviceInfo(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
mDeviceInfo = info;
}
// Returns true if the logical address is same as the argument.
boolean isAddressOf(int addr) {
+ assertRunOnServiceThread();
return addr == mAddress;
}
// Resets the logical address to unregistered(15), meaning the logical device is invalid.
void clearAddress() {
+ assertRunOnServiceThread();
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
void setPreferredAddress(int addr) {
+ assertRunOnServiceThread();
mPreferredAddress = addr;
}
int getPreferredAddress() {
+ assertRunOnServiceThread();
return mPreferredAddress;
}
+
+ void addAndStartAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.add(action);
+ action.start();
+ }
+
+ // See if we have an action of a given type in progress.
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Returns all actions matched with given class type.
+ <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ ArrayList<T> actions = new ArrayList<>();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ actions.add((T) action);
+ }
+ }
+ return actions;
+ }
+
+ /**
+ * Remove the given {@link FeatureAction} object from the action queue.
+ *
+ * @param action {@link FeatureAction} to remove
+ */
+ void removeAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.remove(action);
+ }
+
+ // Remove all actions matched with the given Class type.
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
+ }
+ }
+ }
+
+ protected void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mService.getServiceLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ /**
+ * Called when a hot-plug event issued.
+ *
+ * @param portId id of port where a hot-plug event happened
+ * @param connected whether to connected or not on the event
+ */
+ void onHotplug(int portId, boolean connected) {
+ }
+
+ final HdmiControlService getService() {
+ return mService;
+ }
+
+ final boolean isConnectedToArcPort(int path) {
+ return mService.isConnectedToArcPort(path);
+ }
+
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ /**
+ * Returns the active routing path.
+ */
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the ID of the active HDMI port. The active input is the port that has the active
+ * routing path connected directly or indirectly under the device hierarchy.
+ */
+ int getActiveInput() {
+ synchronized (mLock) {
+ return mService.pathToPortId(mActiveRoutingPath);
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
+ boolean isInPresetInstallationMode() {
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ /**
+ * Whether the given path is located in the tail of current active path.
+ *
+ * @param path to be tested
+ * @return true if the given path is located in the tail of current active path; otherwise,
+ * false
+ */
+ // TODO: move this to local device tv.
+ boolean isTailOfActivePath(int path) {
+ synchronized (mLock) {
+ // If active routing path is internal source, return false.
+ if (mActiveRoutingPath == 0) {
+ return false;
+ }
+ for (int i = 12; i >= 0; i -= 4) {
+ int curActivePath = (mActiveRoutingPath >> i) & 0xF;
+ if (curActivePath == 0) {
+ return true;
+ } else {
+ int curPath = (path >> i) & 0xF;
+ if (curPath != curActivePath) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ HdmiCecMessageCache getCecMessageCache() {
+ assertRunOnServiceThread();
+ return mCecMessageCache;
+ }
+
+ int pathToPortId(int newPath) {
+ assertRunOnServiceThread();
+ return mService.pathToPortId(newPath);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d79e283..01345ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -17,11 +17,15 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Represent a logical device of type Playback residing in Android system.
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevicePlayback";
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiCec.DEVICE_PLAYBACK);
@@ -32,4 +36,54 @@
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
}
+
+ void oneTouchPlay(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+
+ // TODO: Consider the case of multiple TV sets. For now we always direct the command
+ // to the primary one.
+ OneTouchPlayAction action = OneTouchPlayAction.create(this, HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ void queryDisplayStatus(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @Override
+ void onHotplug(int portId, boolean connected) {
+ // TODO: clear devices connected to the given port id.
+ mCecMessageCache.flushAll();
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 625b256..92ddd3d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -22,9 +22,12 @@
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -35,12 +38,25 @@
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
+ // Whether ARC is "enabled" or not.
+ @GuardedBy("mLock")
+ private boolean mArcStatusEnabled = false;
+
+ @GuardedBy("mLock")
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
// TODO: vendor-specific initialization here.
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
@@ -59,14 +75,14 @@
* @param callback callback object to report the result with
*/
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
if (targetDevice == null) {
invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
return;
}
- mService.removeAction(DeviceSelectAction.class);
- mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
- mService.getPhysicalAddress(), targetDevice, callback));
+ removeAction(DeviceSelectAction.class);
+ addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
}
private static void invokeCallback(IHdmiControlCallback callback, int result) {
@@ -79,6 +95,7 @@
@Override
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
mAddress, Locale.getDefault().getISO3Language());
// TODO: figure out how to handle failed to get language code.
@@ -92,8 +109,9 @@
@Override
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+ assertRunOnServiceThread();
// Ignore if [Device Discovery Action] is going on.
- if (mService.hasAction(DeviceDiscoveryAction.class)) {
+ if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ "because Device Discovery Action is on-going:" + message);
return true;
@@ -104,16 +122,16 @@
// If it is a new device and connected to the tail of active path,
// it's required to change routing path.
- boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress)
- && mService.isTailOfActivePath(physicalAddress);
- mService.addAndStartAction(new NewDeviceAction(mService,
- mAddress, message.getSource(), physicalAddress,
+ boolean requireRoutingChange = !isInDeviceList(physicalAddress, logicalAddress)
+ && isTailOfActivePath(physicalAddress);
+ addAndStartAction(new NewDeviceAction(this, message.getSource(), physicalAddress,
requireRoutingChange));
return true;
}
@Override
protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
+ assertRunOnServiceThread();
List<VendorSpecificAction> actions = Collections.emptyList();
// TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
@@ -138,27 +156,304 @@
}
private void launchDeviceDiscovery() {
- mService.clearAllDeviceInfo();
- // TODO: Move the following callback to HdmiLocalDeviceTv.
- DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
+ assertRunOnServiceThread();
+ clearDeviceInfoList();
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryCallback() {
@Override
public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
for (HdmiCecDeviceInfo info : deviceInfos) {
- mService.addCecDevice(info);
+ addCecDevice(info);
}
// Since we removed all devices when it's start and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
- mService.addCecDevice(device.getDeviceInfo());
+ addCecDevice(device.getDeviceInfo());
}
- mService.addAndStartAction(new HotplugDetectionAction(mService,
- mAddress));
+ addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
}
});
- mService.addAndStartAction(action);
+ addAndStartAction(action);
+ }
+
+ // Clear all device info.
+ private void clearDeviceInfoList() {
+ assertRunOnServiceThread();
+ mDeviceInfos.clear();
+ }
+
+ void setSystemAudioMode(boolean on) {
+ synchronized (mLock) {
+ if (on != mSystemAudioMode) {
+ mSystemAudioMode = on;
+ // TODO: Need to set the preference for SystemAudioMode.
+ // TODO: Need to handle the notification of changing the mode and
+ // to identify the notification should be handled in the service or TvSettings.
+ }
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ synchronized (mLock) {
+ assertRunOnServiceThread();
+ return mSystemAudioMode;
+ }
+ }
+
+ /**
+ * Change ARC status into the given {@code enabled} status.
+ *
+ * @return {@code true} if ARC was in "Enabled" status
+ */
+ boolean setArcStatus(boolean enabled) {
+ synchronized (mLock) {
+ boolean oldStatus = mArcStatusEnabled;
+ // 1. Enable/disable ARC circuit.
+ mService.setAudioReturnChannel(enabled);
+
+ // TODO: notify arc mode change to AudioManager.
+
+ // 2. Update arc status;
+ mArcStatusEnabled = enabled;
+ return oldStatus;
+ }
+ }
+
+ /**
+ * Returns whether ARC is enabled or not.
+ */
+ boolean getArcStatus() {
+ synchronized (mLock) {
+ return mArcStatusEnabled;
+ }
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ mService.setAudioStatus(mute, volume);
+ }
+
+ @Override
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // In case where <Initiate Arc> is started by <Request ARC Initiation>
+ // need to clean up RequestArcInitiationAction.
+ removeAction(RequestArcInitiationAction.class);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+ message.getSource(), true);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // In case where <Terminate Arc> is started by <Request ARC Termination>
+ // need to clean up RequestArcInitiationAction.
+ // TODO: check conditions of power status by calling is_connected api
+ // to be added soon.
+ removeAction(RequestArcTerminationAction.class);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+ message.getSource(), false);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+ message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ return true;
+ }
+
+ private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+ if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+ || message.getDestination() != HdmiCec.ADDR_TV
+ || getAvrDeviceInfo() == null) {
+ Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
+ * logical address as new device info's.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
+ * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
+ * that has the same logical address as new one has.
+ */
+ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
+ if (oldDeviceInfo != null) {
+ removeDeviceInfo(deviceInfo.getLogicalAddress());
+ }
+ mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+ return oldDeviceInfo;
+ }
+
+ /**
+ * Remove a device info corresponding to the given {@code logicalAddress}.
+ * It returns removed {@link HdmiCecDeviceInfo} if exists.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address of device to be removed
+ * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
+ */
+ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
+ if (deviceInfo != null) {
+ mDeviceInfos.remove(logicalAddress);
+ }
+ return deviceInfo;
+ }
+
+ /**
+ * Return a list of all {@link HdmiCecDeviceInfo}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ */
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includelLocalDevice) {
+ assertRunOnServiceThread();
+ if (includelLocalDevice) {
+ return HdmiUtils.sparseArrayToList(mDeviceInfos);
+ } else {
+
+ ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (!isLocalDeviceAddress(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
+ }
+
+ private boolean isLocalDeviceAddress(int address) {
+ assertRunOnServiceThread();
+ for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+ if (device.isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address to be retrieved
+ * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
+ * Returns null if no logical address matched
+ */
+ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ return mDeviceInfos.get(logicalAddress);
+ }
+
+ HdmiCecDeviceInfo getAvrDeviceInfo() {
+ assertRunOnServiceThread();
+ return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+ }
+
+ /**
+ * Called when a device is newly added or a new device is detected.
+ *
+ * @param info device info of a new device.
+ */
+ final void addCecDevice(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
+ addDeviceInfo(info);
+
+ // TODO: announce new device detection.
+ }
+
+ /**
+ * Called when a device is removed or removal of device is detected.
+ *
+ * @param address a logical address of a device to be removed
+ */
+ final void removeCecDevice(int address) {
+ assertRunOnServiceThread();
+ removeDeviceInfo(address);
+ mCecMessageCache.flushMessagesFrom(address);
+
+ // TODO: announce a device removal.
+ }
+
+ /**
+ * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
+ * the given routing path. CEC devices use routing path for its physical address to
+ * describe the hierarchy of the devices in the network.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
+ */
+ final HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
+ assertRunOnServiceThread();
+ for (HdmiCecDeviceInfo info : getDeviceInfoList(false)) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Whether a device of the specified physical address and logical address exists
+ * in a device info list. However, both are minimal condition and it could
+ * be different device from the original one.
+ *
+ * @param physicalAddress physical address of a device to be searched
+ * @param logicalAddress logical address of a device to be searched
+ * @return true if exist; otherwise false
+ */
+ boolean isInDeviceList(int physicalAddress, int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
+ if (device == null) {
+ return false;
+ }
+ return device.getPhysicalAddress() == physicalAddress;
+ }
+
+ @Override
+ void onHotplug(int portNo, boolean connected) {
+ assertRunOnServiceThread();
+ // TODO: delegate onHotplug event to each local device.
+
+ // Tv device will have permanent HotplugDetectionAction.
+ List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
+ if (!hotplugActions.isEmpty()) {
+ // Note that hotplug action is single action running on a machine.
+ // "pollAllDevicesNow" cleans up timer and start poll action immediately.
+ hotplugActions.get(0).pollAllDevicesNow();
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 5fffada..1c53b58 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -37,14 +37,11 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -101,10 +98,6 @@
// and sparse call it shares a thread to handle IO operations.
private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
- // A collection of FeatureAction.
- // Note that access to this collection should happen in service thread.
- private final LinkedList<FeatureAction> mActions = new LinkedList<>();
-
// Used to synchronize the access to the service.
private final Object mLock = new Object();
@@ -130,8 +123,6 @@
// Handler running on service thread. It's used to run a task in service thread.
private final Handler mHandler = new Handler();
- private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
-
@Nullable
private HdmiCecController mCecController;
@@ -142,37 +133,10 @@
// from being modified.
private List<HdmiPortInfo> mPortInfo;
- // Logical address of the active source.
- @GuardedBy("mLock")
- private int mActiveSource;
-
- // Active routing path. Physical address of the active source but not all the time, such as
- // when the new active source does not claim itself to be one. Note that we don't keep
- // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
- @GuardedBy("mLock")
- private int mActiveRoutingPath;
-
- // Set to true while the service is in normal mode. While set to false, no input change is
- // allowed. Used for situations where input change can confuse users such as channel auto-scan,
- // system upgrade, etc., a.k.a. "prohibit mode".
- @GuardedBy("mLock")
- private boolean mInputChangeEnabled;
-
- @GuardedBy("mLock")
- // Whether ARC is "enabled" or not.
- // TODO: it may need to hold lock if it's accessed from others.
- private boolean mArcStatusEnabled = false;
-
- @GuardedBy("mLock")
- // Whether SystemAudioMode is "On" or not.
- private boolean mSystemAudioMode;
-
public HdmiControlService(Context context) {
super(context);
mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
- // TODO: Get control flag from persistent storage
- mInputChangeEnabled = true;
}
@Override
@@ -345,44 +309,6 @@
return mHandler.getLooper();
}
- int getActiveSource() {
- synchronized (mLock) {
- return mActiveSource;
- }
- }
-
- /**
- * Returns the active routing path.
- */
- int getActivePath() {
- synchronized (mLock) {
- return mActiveRoutingPath;
- }
- }
-
- /**
- * Returns the ID of the active HDMI port. The active input is the port that has the active
- * routing path connected directly or indirectly under the device hierarchy.
- */
- int getActiveInput() {
- synchronized (mLock) {
- return pathToPortId(mActiveRoutingPath);
- }
- }
-
- void updateActiveDevice(int logicalAddress, int physicalAddress) {
- synchronized (mLock) {
- mActiveSource = logicalAddress;
- mActiveRoutingPath = physicalAddress;
- }
- }
-
- void setInputChangeEnabled(boolean enabled) {
- synchronized (mLock) {
- mInputChangeEnabled = enabled;
- }
- }
-
/**
* Returns physical address of the device.
*/
@@ -399,7 +325,11 @@
HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
- return mCecController.getDeviceInfo(logicalAddress);
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv == null) {
+ return null;
+ }
+ return tv.getDeviceInfo(logicalAddress);
}
/**
@@ -410,67 +340,6 @@
}
/**
- * Returns a list of {@link HdmiCecDeviceInfo}.
- *
- * @param includeLocalDevice whether to include local devices
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- return mCecController.getDeviceInfoList(includeLocalDevice);
- }
-
- /**
- * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
- * the given routing path. CEC devices use routing path for its physical address to
- * describe the hierarchy of the devices in the network.
- *
- * @param path routing path or physical address
- * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
- */
- HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
- assertRunOnServiceThread();
- for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
- if (info.getPhysicalAddress() == path) {
- return info;
- }
- }
- return null;
- }
-
- /**
- * Add and start a new {@link FeatureAction} to the action queue.
- *
- * @param action {@link FeatureAction} to add and start
- */
- void addAndStartAction(final FeatureAction action) {
- // TODO: may need to check the number of stale actions.
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- mActions.add(action);
- action.start();
- }
- });
- }
-
- void setSystemAudioMode(boolean on) {
- synchronized (mLock) {
- if (on != mSystemAudioMode) {
- mSystemAudioMode = on;
- // TODO: Need to set the preference for SystemAudioMode.
- // TODO: Need to handle the notification of changing the mode and
- // to identify the notification should be handled in the service or TvSettings.
- }
- }
- }
-
- boolean getSystemAudioMode() {
- synchronized (mLock) {
- return mSystemAudioMode;
- }
- }
-
- /**
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
@@ -483,57 +352,7 @@
return false;
}
- // See if we have an action of a given type in progress.
- <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- return true;
- }
- }
- return false;
- }
-
- // Returns all actions matched with given class type.
- <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
- ArrayList<T> actions = new ArrayList<>();
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- actions.add((T) action);
- }
- }
- return actions;
- }
-
- /**
- * Remove the given {@link FeatureAction} object from the action queue.
- *
- * @param action {@link FeatureAction} to remove
- */
- void removeAction(final FeatureAction action) {
- assertRunOnServiceThread();
- mActions.remove(action);
- }
-
- // Remove all actions matched with the given Class type.
- <T extends FeatureAction> void removeAction(final Class<T> clazz) {
- removeActionExcept(clazz, null);
- }
-
- // Remove all actions matched with the given Class type besides |exception|.
- <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
- final FeatureAction exception) {
- assertRunOnServiceThread();
- Iterator<FeatureAction> iter = mActions.iterator();
- while (iter.hasNext()) {
- FeatureAction action = iter.next();
- if (action != exception && action.getClass().equals(clazz)) {
- action.clear();
- mActions.remove(action);
- }
- }
- }
-
- private void runOnServiceThread(Runnable runnable) {
+ void runOnServiceThread(Runnable runnable) {
mHandler.post(runnable);
}
@@ -548,35 +367,6 @@
}
/**
- * Change ARC status into the given {@code enabled} status.
- *
- * @return {@code true} if ARC was in "Enabled" status
- */
- boolean setArcStatus(boolean enabled) {
- assertRunOnServiceThread();
- synchronized (mLock) {
- boolean oldStatus = mArcStatusEnabled;
- // 1. Enable/disable ARC circuit.
- mCecController.setAudioReturnChannel(enabled);
-
- // TODO: notify arc mode change to AudioManager.
-
- // 2. Update arc status;
- mArcStatusEnabled = enabled;
- return oldStatus;
- }
- }
-
- /**
- * Returns whether ARC is enabled or not.
- */
- boolean getArcStatus() {
- synchronized (mLock) {
- return mArcStatusEnabled;
- }
- }
-
- /**
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
@@ -591,47 +381,17 @@
}
boolean handleCecCommand(HdmiCecMessage message) {
- // Cache incoming message. Note that it caches only white-listed one.
- mCecMessageCache.cacheMessage(message);
-
- // Commands that queries system information replies directly instead
- // of creating FeatureAction because they are state-less.
- // TODO: move the leftover message to local device.
- switch (message.getOpcode()) {
- case HdmiCec.MESSAGE_INITIATE_ARC:
- handleInitiateArc(message);
- return true;
- case HdmiCec.MESSAGE_TERMINATE_ARC:
- handleTerminateArc(message);
- return true;
- case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- handleSetSystemAudioMode(message);
- return true;
- case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
- handleSystemAudioModeStatus(message);
- return true;
- default:
- if (dispatchMessageToAction(message)) {
- return true;
- }
- break;
- }
-
return dispatchMessageToLocalDevice(message);
}
- private boolean dispatchMessageToAction(HdmiCecMessage message) {
- for (FeatureAction action : mActions) {
- if (action.processCommand(message)) {
- return true;
- }
- }
- return false;
+ void setAudioReturnChannel(boolean enabled) {
+ mCecController.setAudioReturnChannel(enabled);
}
private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- if (device.dispatchMessage(message)) {
+ if (device.dispatchMessage(message)
+ && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
return true;
}
}
@@ -648,14 +408,9 @@
*/
void onHotplug(int portNo, boolean connected) {
assertRunOnServiceThread();
- // TODO: delegate onHotplug event to each local device.
- // Tv device will have permanent HotplugDetectionAction.
- List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
- if (!hotplugActions.isEmpty()) {
- // Note that hotplug action is single action running on a machine.
- // "pollAllDevicesNow" cleans up timer and start poll action immediately.
- hotplugActions.get(0).pollAllDevicesNow();
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ device.onHotplug(portNo, connected);
}
announceHotplugEvent(portNo, connected);
@@ -686,32 +441,17 @@
return strategy | iterationStrategy;
}
- void clearAllDeviceInfo() {
- assertRunOnServiceThread();
- mCecController.clearDeviceInfoList();
- }
-
List<HdmiCecLocalDevice> getAllLocalDevices() {
assertRunOnServiceThread();
return mCecController.getLocalDeviceList();
}
- /**
- * Whether a device of the specified physical address and logical address exists
- * in a device info list. However, both are minimal condition and it could
- * be different device from the original one.
- *
- * @param physicalAddress physical address of a device to be searched
- * @param logicalAddress logical address of a device to be searched
- * @return true if exist; otherwise false
- */
- boolean isInDeviceList(int physicalAddress, int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress);
- if (device == null) {
- return false;
- }
- return device.getPhysicalAddress() == physicalAddress;
+ Object getServiceLock() {
+ return mLock;
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ // TODO: Hook up with AudioManager.
}
private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
@@ -721,53 +461,6 @@
getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
- private void handleInitiateArc(HdmiCecMessage message){
- // In case where <Initiate Arc> is started by <Request ARC Initiation>
- // need to clean up RequestArcInitiationAction.
- removeAction(RequestArcInitiationAction.class);
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
- message.getDestination(), message.getSource(), true);
- addAndStartAction(action);
- }
-
- private void handleTerminateArc(HdmiCecMessage message) {
- // In case where <Terminate Arc> is started by <Request ARC Termination>
- // need to clean up RequestArcInitiationAction.
- // TODO: check conditions of power status by calling is_connected api
- // to be added soon.
- removeAction(RequestArcTerminationAction.class);
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
- message.getDestination(), message.getSource(), false);
- addAndStartAction(action);
- }
-
- private void handleSetSystemAudioMode(HdmiCecMessage message) {
- if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
- return;
- }
- SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
- message.getDestination(), message.getSource(),
- HdmiUtils.parseCommandParamSystemAudioStatus(message));
- addAndStartAction(action);
- }
-
- private void handleSystemAudioModeStatus(HdmiCecMessage message) {
- if (!isMessageForSystemAudio(message)) {
- return;
- }
- setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
- }
-
- private boolean isMessageForSystemAudio(HdmiCecMessage message) {
- if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
- || message.getDestination() != HdmiCec.ADDR_TV
- || getAvrDeviceInfo() == null) {
- Slog.w(TAG, "Skip abnormal CecMessage: " + message);
- return false;
- }
- return true;
- }
-
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -824,8 +517,7 @@
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDeviceTv tv =
- (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
@@ -897,7 +589,7 @@
}
@Override
- public void sendKeyEvent(int keyCode) {
+ public void sendKeyEvent(int keyCode, boolean isPressed) {
// TODO: Implement this
}
@@ -908,51 +600,26 @@
}
}
- private void oneTouchPlay(IHdmiControlCallback callback) {
- if (hasAction(OneTouchPlayAction.class)) {
- Slog.w(TAG, "oneTouchPlay already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void oneTouchPlay(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- // TODO: Consider the case of multiple TV sets. For now we always direct the command
- // to the primary one.
- OneTouchPlayAction action = OneTouchPlayAction.create(this,
- source.getDeviceInfo().getLogicalAddress(),
- source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate oneTouchPlay");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.oneTouchPlay(callback);
}
- private void queryDisplayStatus(IHdmiControlCallback callback) {
- if (hasAction(DevicePowerStatusAction.class)) {
- Slog.w(TAG, "queryDisplayStatus already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void queryDisplayStatus(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
- source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate queryDisplayStatus");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.queryDisplayStatus(callback);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -1003,43 +670,6 @@
}
}
- HdmiCecDeviceInfo getAvrDeviceInfo() {
- return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
- }
-
- void setAudioStatus(boolean mute, int volume) {
- // TODO: Hook up with AudioManager.
- }
-
- boolean isInPresetInstallationMode() {
- synchronized (mLock) {
- return !mInputChangeEnabled;
- }
- }
-
- /**
- * Called when a device is newly added or a new device is detected.
- *
- * @param info device info of a new device.
- */
- void addCecDevice(HdmiCecDeviceInfo info) {
- mCecController.addDeviceInfo(info);
-
- // TODO: announce new device detection.
- }
-
- /**
- * Called when a device is removed or removal of device is detected.
- *
- * @param address a logical address of a device to be removed
- */
- void removeCecDevice(int address) {
- mCecController.removeDeviceInfo(address);
- mCecMessageCache.flushMessagesFrom(address);
-
- // TODO: announce a device removal.
- }
-
private void announceHotplugEvent(int portNo, boolean connected) {
HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
synchronized (mLock) {
@@ -1058,39 +688,16 @@
}
}
- HdmiCecMessageCache getCecMessageCache() {
- return mCecMessageCache;
- }
-
private static boolean hasSameTopPort(int path1, int path2) {
return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
== (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
}
- /**
- * Whether the given path is located in the tail of current active path.
- *
- * @param path to be tested
- * @return true if the given path is located in the tail of current active path; otherwise,
- * false
- */
- // TODO: move this to local device tv.
- boolean isTailOfActivePath(int path) {
- // If active routing path is internal source, return false.
- if (mActiveRoutingPath == 0) {
- return false;
- }
- for (int i = 12; i >= 0; i -= 4) {
- int curActivePath = (mActiveRoutingPath >> i) & 0xF;
- if (curActivePath == 0) {
- return true;
- } else {
- int curPath = (path >> i) & 0xF;
- if (curPath != curActivePath) {
- return false;
- }
- }
- }
- return false;
+ private HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ }
+
+ private HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index b534377..9b7cc8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -19,6 +19,7 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
@@ -111,4 +112,13 @@
static int threeBytesToInt(byte[] data) {
return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
}
+
+ static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+ ArrayList<T> list = new ArrayList<>();
+ for (int i = 0; i < array.size(); ++i) {
+ list.add(array.valueAt(i));
+ }
+ return list;
+ }
+
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index ae20eda..74f40a6 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -52,11 +52,10 @@
/**
* Constructor
*
- * @param service instance of {@link HdmiControlService}
- * @param sourceAddress logical address of a device that initiate this action
+ * @param source {@link HdmiCecLocalDevice} instance
*/
- HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ HotplugDetectionAction(HdmiCecLocalDevice source) {
+ super(source);
}
@Override
@@ -110,7 +109,7 @@
if (mTimeoutCount == 0) {
pollAllDevices();
} else {
- if (mService.getSystemAudioMode()) {
+ if (tv().getSystemAudioMode()) {
pollAudioSystem();
}
}
@@ -121,7 +120,7 @@
private void pollAllDevices() {
Slog.v(TAG, "Poll all devices.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, false);
@@ -133,7 +132,7 @@
private void pollAudioSystem() {
Slog.v(TAG, "Poll audio system.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, true);
@@ -143,7 +142,7 @@
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
@@ -195,7 +194,8 @@
private void addDevice(int addedAddress) {
// Send <Give Physical Address>.
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
+ addedAddress));
}
private void removeDevice(int removedAddress) {
@@ -206,7 +206,7 @@
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
- mService.removeCecDevice(removedAddress);
+ tv().removeCecDevice(removedAddress);
}
private void mayChangeRoutingPath(int address) {
@@ -217,7 +217,7 @@
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+ List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
if (actions.isEmpty()) {
return;
}
@@ -225,7 +225,7 @@
// Should ave only one Device Select Action
DeviceSelectAction action = actions.get(0);
if (action.getTargetAddress() == address) {
- mService.removeAction(DeviceSelectAction.class);
+ removeAction(DeviceSelectAction.class);
}
}
@@ -239,11 +239,9 @@
}
// Turn off system audio mode.
- mService.setSystemAudioMode(false);
- if (mService.getArcStatus()) {
- mService.addAndStartAction(
- new RequestArcTerminationAction(mService, mSourceAddress, address));
+ tv().setSystemAudioMode(false);
+ if (tv().getArcStatus()) {
+ addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
}
-
}
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 2cae507..4a49f09 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -56,15 +56,14 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param deviceLogicalAddress logical address of the device in interest
* @param devicePhysicalAddress physical address of the device in interest
* @param requireRoutingChange whether to initiate routing change or not
*/
- NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+ NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress,
int devicePhysicalAddress, boolean requireRoutingChange) {
- super(service, sourceAddress);
+ super(source);
mDeviceLogicalAddress = deviceLogicalAddress;
mDevicePhysicalAddress = devicePhysicalAddress;
mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
@@ -73,16 +72,16 @@
@Override
public boolean start() {
- if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
- if (mService.getAvrDeviceInfo() == null) {
+ if (HdmiCec.getTypeFromAddress(getSourceAddress()) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ if (tv().getAvrDeviceInfo() == null) {
// TODO: Start system audio initiation action
}
// If new device is connected through ARC enabled port,
// initiates ARC channel establishment.
- if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) {
- mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress,
- mDeviceLogicalAddress));
+ if (tv().isConnectedToArcPort(mDevicePhysicalAddress)) {
+ addAndStartAction(new RequestArcInitiationAction(localDevice(),
+ mDeviceLogicalAddress));
}
}
@@ -95,7 +94,7 @@
return true;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
return true;
@@ -155,14 +154,14 @@
private void startRoutingChange() {
// Stop existing routing control.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
// Send routing change. The the address is a path of the active port.
int newPath = toTopMostPortPath(mDevicePhysicalAddress);
- sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
- mService.getActivePath(), newPath));
- mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress,
- mService.pathToPortId(newPath), null));
+ sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+ localDevice().getActivePath(), newPath));
+ addAndStartAction(new RoutingControlAction(localDevice(),
+ localDevice().pathToPortId(newPath), null));
}
private static int toTopMostPortPath(int physicalAddress) {
@@ -170,7 +169,7 @@
}
private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode);
if (message != null) {
return processCommand(message);
}
@@ -184,7 +183,7 @@
if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
}
@@ -193,7 +192,7 @@
if (mDisplayName == null) {
mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
}
- mService.addCecDevice(new HdmiCecDeviceInfo(
+ tv().addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 69fad13..e0a3a8b 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -30,7 +30,7 @@
* <p>Package-private, accessed by {@link HdmiControlService} only.
*/
-public final class OneTouchPlayAction extends FeatureAction {
+final class OneTouchPlayAction extends FeatureAction {
private static final String TAG = "OneTouchPlayAction";
// State in which the action is waiting for <Report Power Status>. In normal situation
@@ -48,34 +48,32 @@
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
- private final int mSourcePath;
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
private int mPowerStatusCounter = 0;
// Factory method. Ensures arguments are valid.
- static OneTouchPlayAction create(HdmiControlService service, int sourceAddress,
- int sourcePath, int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ static OneTouchPlayAction create(HdmiCecLocalDevice source,
+ int targetAddress, IHdmiControlCallback callback) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new OneTouchPlayAction(service, sourceAddress, sourcePath, targetAddress, callback);
+ return new OneTouchPlayAction(source, targetAddress,
+ callback);
}
- private OneTouchPlayAction(HdmiControlService service, int sourceAddress, int sourcePath,
- int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
- mSourcePath = sourcePath;
+ private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback) {
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@Override
boolean start() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildTextViewOn(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
@@ -84,13 +82,12 @@
}
private void broadcastActiveSource() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath));
+ sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 08ca306..a2e08f1 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -35,16 +35,14 @@
/**
* @Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address. It should
- * TV type
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress
* is invalid
*/
- RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
@@ -72,9 +70,9 @@
protected final void disableArcTransmission() {
// Start Set ARC Transmission State action.
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService,
- mSourceAddress, mAvrAddress, false);
- mService.addAndStartAction(action);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(localDevice(),
+ mAvrAddress, false);
+ addAndStartAction(action);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 343aff7..98ec953 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -31,14 +31,14 @@
*
* For more details look at {@link RequestArcAction#RequestArcAction}.
*/
- RequestArcInitiationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
- mAvrAddress);
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index d4a35f8..19fa22b 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -31,14 +31,14 @@
*
* @see RequestArcAction#RequestArcAction
*/
- RequestArcTerminationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 19974ea..2eec846 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -39,7 +39,7 @@
* <li> Routing at CEC enable time
* </ul>
*/
-public class RoutingControlAction extends FeatureAction {
+final class RoutingControlAction extends FeatureAction {
private static final String TAG = "RoutingControlAction";
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
@@ -63,9 +63,8 @@
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiControlService service, int sourceAddress, int path,
- IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
+ super(localDevice);
mCallback = callback;
mCurrentRoutingPath = path;
}
@@ -92,7 +91,7 @@
}
mCurrentRoutingPath = routingPath;
// Stop possible previous routing change sequence if in progress.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
} else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
@@ -130,7 +129,8 @@
}
private void sendSetStreamPath() {
- sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+ mCurrentRoutingPath));
}
private static boolean isInActiveRoutingPath(int activePath, int newPath) {
@@ -164,9 +164,9 @@
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+ HdmiCecDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
if (device == null) {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(tv().pathToPortId(mCurrentRoutingPath));
} else {
// TODO: Also check followings and then proceed:
// if routing change was neither triggered by TV at CEC enable time, nor
@@ -184,7 +184,7 @@
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
int tvPowerStatus = getTvPowerStatus();
if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
- if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
+ if (!maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath))) {
sendSetStreamPath();
}
}
@@ -196,7 +196,7 @@
// Called whenever an HDMI input of the TV shall become the active input.
private boolean maybeChangeActiveInput(int path) {
- if (mService.getActiveInput() == mService.pathToPortId(path)) {
+ if (localDevice().getActiveInput() == localDevice().pathToPortId(path)) {
return false;
}
// TODO: Remember the currently active input
@@ -207,7 +207,7 @@
}
private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
callback);
}
@@ -216,7 +216,7 @@
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
} else {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 8e6998f..ddcda92 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -16,10 +16,8 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
-import android.view.KeyEvent;
import android.util.Slog;
-
-import libcore.util.EmptyArray;
+import android.view.KeyEvent;
/**
* Feature action that transmits remote control key command (User Control Press/
@@ -56,13 +54,12 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param targetAddress logical address of the device to send the keys to
* @param keyCode remote control key code as defined in {@link KeyEvent}
*/
- SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
- super(service, sourceAddress);
+ SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+ super(source);
mTargetAddress = targetAddress;
mLastKeyCode = keyCode;
}
@@ -112,12 +109,13 @@
if (keyCodeAndParam == null) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
- keyCodeAndParam));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, keyCodeAndParam));
}
private void sendKeyUp() {
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index d53d88d..da841c6 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -39,14 +39,13 @@
/**
* @Constructor
*
- * @param service an instance of {@link HdmiControlService}
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param enabled whether to enable ARC Transmission channel
*/
- SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
boolean enabled) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
@@ -65,7 +64,7 @@
private void sendReportArcInitiated() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
@@ -93,14 +92,14 @@
}
private void setArcStatus(boolean enabled) {
- boolean wasEnabled = mService.setArcStatus(enabled);
+ boolean wasEnabled = tv().setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
// If enabled before and set to "disabled" and send <Report Arc Terminated> to
// av reciever.
if (!enabled && wasEnabled) {
- sendCommand(
- HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
+ sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
+ mAvrAddress));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index dde3342..cf7ff90 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -47,23 +47,22 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of source device (TV or STB).
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
*/
- SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
- boolean targetStatus) {
- super(service, sourceAddress);
+ SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) {
+ super(source);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrLogicalAddress = avrAddress;
mTargetAudioStatus = targetStatus;
}
protected void sendSystemAudioModeRequest() {
- int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+ int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ getSourceAddress(),
mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
@@ -90,11 +89,11 @@
}
protected void setSystemAudioMode(boolean mode) {
- mService.setSystemAudioMode(mode);
+ tv().setSystemAudioMode(mode);
}
protected void sendGiveAudioStatus() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(),
mAvrLogicalAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
@@ -112,7 +111,7 @@
private void handleSendGiveAudioStatusFailure() {
// TODO: Notify the failure status.
- int uiCommand = mService.getSystemAudioMode()
+ int uiCommand = tv().getSystemAudioMode()
? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
: HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF
sendUserControlPressedAndReleased(uiCommand);
@@ -121,9 +120,9 @@
private void sendUserControlPressedAndReleased(int uiCommand) {
sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
- mSourceAddress, mAvrLogicalAddress, uiCommand));
+ getSourceAddress(), mAvrLogicalAddress, uiCommand));
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- mSourceAddress, mAvrLogicalAddress));
+ getSourceAddress(), mAvrLogicalAddress));
}
@Override
@@ -158,7 +157,7 @@
if (params.length > 0) {
boolean mute = (params[0] & 0x80) == 0x80;
int volume = params[0] & 0x7F;
- mService.setAudioStatus(mute, volume);
+ tv().setAudioStatus(mute, volume);
if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
// Toggle AVR's mute status to match with the system audio status.
sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
@@ -171,8 +170,8 @@
}
protected void removeSystemAudioActionInProgress() {
- mService.removeActionExcept(SystemAudioActionFromTv.class, this);
- mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+ removeActionExcept(SystemAudioActionFromTv.class, this);
+ removeActionExcept(SystemAudioActionFromAvr.class, this);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index c5eb44b..3907b71 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -25,16 +25,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
*/
- SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(source, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
@@ -45,13 +44,13 @@
}
private void handleSystemAudioActionFromAvr() {
- if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ if (mTargetAudioStatus == tv().getSystemAudioMode()) {
finish();
return;
}
- if (mService.isInPresetInstallationMode()) {
+ if (tv().isInPresetInstallationMode()) {
sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
- mSourceAddress, mAvrLogicalAddress,
+ getSourceAddress(), mAvrLogicalAddress,
HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
mTargetAudioStatus = false;
sendSystemAudioModeRequest();
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 9994de6..e0c4ff4 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -26,16 +26,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param sourceAddress {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress is invalid
*/
- SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(sourceAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
index 9d45702..c954b50 100644
--- a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
+++ b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
@@ -12,8 +12,8 @@
private static final int STATE_1 = 1;
private static final int STATE_2 = 2;
- VendorSpecificAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ VendorSpecificAction(HdmiCecLocalDevice source) {
+ super(source);
// Modify the constructor if additional arguments are necessary.
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 06732ca..825dc84 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2268,16 +2268,15 @@
// lock on mNotificationList
int indexOfNotificationLocked(String key) {
- NotificationRecord r = mNotificationsByKey.get(key);
- if (r == null) {
- return -1;
+ final int N = mNotificationList.size();
+ for (int i = 0; i < N; i++) {
+ if (key.equals(mNotificationList.get(i).getKey())) {
+ return i;
+ }
}
- int index = Collections.binarySearch(mNotificationList, r, mRankingComparator);
- // Guarantee to return -1 when not found.
- return (index >= 0) ? index : -1;
+ return -1;
}
-
private void updateNotificationPulse() {
synchronized (mNotificationList) {
updateLightsLocked();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 82d3f53..c0f1eec 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -345,6 +345,10 @@
}
}
+ public int pruneDexCache() {
+ return execute("prunedexcache");
+ }
+
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3ed73f7..dd33771 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -28,7 +28,7 @@
import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.ApkLite;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
@@ -297,7 +297,7 @@
// Verify that all staged packages are internally consistent
for (File file : files) {
- final PackageLite info = PackageParser.parsePackageLite(file.getAbsolutePath(),
+ final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(),
PackageParser.PARSE_GET_SIGNATURES);
if (info == null) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
@@ -356,7 +356,7 @@
"Missing existing base package for " + mPackageName);
}
- final PackageLite info = PackageParser.parsePackageLite(app.sourceDir,
+ final ApkLite info = PackageParser.parseApkLite(app.sourceDir,
PackageParser.PARSE_GET_SIGNATURES);
if (info == null) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 49502b6..1209386 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -93,6 +93,7 @@
import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
@@ -136,6 +137,7 @@
import android.system.Os;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -412,9 +414,6 @@
final HashMap<String, SharedLibraryEntry> mSharedLibraries
= new HashMap<String, SharedLibraryEntry>();
- // Temporary for building the final shared libraries for an .apk.
- String[] mTmpSharedLibraries = null;
-
// These are the features this devices supports that were read from the
// etc/permissions.xml file.
final HashMap<String, FeatureInfo> mAvailableFeatures =
@@ -1485,7 +1484,21 @@
}
if (didDexOptLibraryOrTool) {
- pruneDexFiles(new File(dataDir, "dalvik-cache"));
+ // If we dexopted a library or tool, then something on the system has
+ // changed. Consider this significant, and wipe away all other
+ // existing dexopt files to ensure we don't leave any dangling around.
+ //
+ // Additionally, delete all dex files from the root directory
+ // since there shouldn't be any there anyway.
+ //
+ // TODO: This should be revisited because it isn't as good an indicator
+ // as it used to be. It used to include the boot classpath but at some point
+ // DexFile.isDexOptNeeded started returning false for the boot
+ // class path files in all cases. It is very possible in a
+ // small maintenance release update that the library and tool
+ // jars may be unchanged but APK could be removed resulting in
+ // unused dalvik-cache files.
+ mInstaller.pruneDexCache();
}
// Collect vendor overlay packages.
@@ -1727,45 +1740,6 @@
Runtime.getRuntime().gc();
}
- private static void pruneDexFiles(File cacheDir) {
- // If we had to do a dexopt of one of the previous
- // things, then something on the system has changed.
- // Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any
- // dangling around.
- //
- // Additionally, delete all dex files from the root directory
- // since there shouldn't be any there anyway.
- //
- // Note: This isn't as good an indicator as it used to be. It
- // used to include the boot classpath but at some point
- // DexFile.isDexOptNeeded started returning false for the boot
- // class path files in all cases. It is very possible in a
- // small maintenance release update that the library and tool
- // jars may be unchanged but APK could be removed resulting in
- // unused dalvik-cache files.
- File[] files = cacheDir.listFiles();
- if (files != null) {
- for (File file : files) {
- if (!file.isDirectory()) {
- Slog.i(TAG, "Pruning dalvik file: " + file.getAbsolutePath());
- file.delete();
- } else {
- File[] subDirList = file.listFiles();
- if (subDirList != null) {
- for (File subDirFile : subDirList) {
- final String fn = subDirFile.getName();
- if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) {
- Slog.i(TAG, "Pruning dalvik file: " + fn);
- subDirFile.delete();
- }
- }
- }
- }
- }
- }
- }
-
@Override
public boolean isFirstBoot() {
return !mRestoredSettings || mPackageUsage.isFirstBoot();
@@ -2268,8 +2242,7 @@
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
return null;
}
- // TODO: teach about reading split name
- pkg = new PackageParser.Package(packageName, null);
+ pkg = new PackageParser.Package(packageName);
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
@@ -4097,19 +4070,19 @@
private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
PackageParser.Package opkg) {
if (!opkg.mTrustedOverlay) {
- Slog.w(TAG, "Skipping target and overlay pair " + pkg.mScanPath + " and " +
- opkg.mScanPath + ": overlay not trusted");
+ Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " +
+ opkg.codePath + ": overlay not trusted");
return false;
}
HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
if (overlaySet == null) {
- Slog.e(TAG, "was about to create idmap for " + pkg.mScanPath + " and " +
- opkg.mScanPath + " but target package has no known overlays");
+ Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " +
+ opkg.codePath + " but target package has no known overlays");
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- if (mInstaller.idmap(pkg.mScanPath, opkg.mScanPath, sharedGid) != 0) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.mScanPath + " and " + opkg.mScanPath);
+ if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
+ Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
return false;
}
PackageParser.Package[] overlayArray =
@@ -4204,8 +4177,10 @@
Log.i(TAG, srcFile.toString() + " changed; collecting certs");
}
- if (!pp.collectCertificates(pkg, parseFlags)) {
- mLastScanError = pp.getParseError();
+ try {
+ pp.collectCertificates(pkg, parseFlags);
+ } catch (PackageParserException e) {
+ mLastScanError = e.error;
return false;
}
return true;
@@ -4224,11 +4199,13 @@
PackageParser pp = new PackageParser(scanPath);
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
- final PackageParser.Package pkg = pp.parsePackage(scanFile,
- scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
- if (pkg == null) {
- mLastScanError = pp.getParseError();
+ final PackageParser.Package pkg;
+ try {
+ pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags,
+ (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
+ } catch (PackageParserException e) {
+ mLastScanError = e.error;
return null;
}
@@ -4395,12 +4372,13 @@
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
- resPath = pkg.mScanPath;
+ resPath = pkg.codePath;
}
- codePath = pkg.mScanPath;
+ codePath = pkg.codePath;
// Set application objects path explicitly.
- setApplicationInfoPaths(pkg, codePath, resPath);
+ pkg.applicationInfo.sourceDir = codePath;
+ pkg.applicationInfo.publicSourceDir = resPath;
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
@@ -4425,13 +4403,6 @@
return scannedPkg;
}
- private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath,
- String destResPath) {
- pkg.mPath = pkg.mScanPath = destCodePath;
- pkg.applicationInfo.sourceDir = destCodePath;
- pkg.applicationInfo.publicSourceDir = destResPath;
- }
-
private static String fixProcessName(String defProcessName,
String processName, int uid) {
if (processName == null) {
@@ -4657,7 +4628,7 @@
boolean performed = false;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- String path = pkg.mScanPath;
+ String path = pkg.codePath;
try {
boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
pkg.packageName,
@@ -4830,11 +4801,11 @@
return res;
}
- private int addSharedLibraryLPw(final SharedLibraryEntry file, int num,
+ private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
- mTmpSharedLibraries[num] = file.path;
- return num+1;
+ usesLibraryFiles.add(file.path);
+ return;
}
PackageParser.Package p = mPackages.get(file.apk);
if (changingLib != null && changingLib.packageName.equals(file.apk)) {
@@ -4847,16 +4818,8 @@
}
}
if (p != null) {
- String path = p.mPath;
- for (int i=0; i<num; i++) {
- if (mTmpSharedLibraries[i].equals(path)) {
- return num;
- }
- }
- mTmpSharedLibraries[num] = p.mPath;
- return num+1;
+ usesLibraryFiles.add(p.codePath);
}
- return num;
}
private boolean updateSharedLibrariesLPw(PackageParser.Package pkg,
@@ -4870,11 +4833,7 @@
}
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
- if (mTmpSharedLibraries == null ||
- mTmpSharedLibraries.length < mSharedLibraries.size()) {
- mTmpSharedLibraries = new String[mSharedLibraries.size()];
- }
- int num = 0;
+ final ArraySet<String> usesLibraryFiles = new ArraySet<>();
int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
for (int i=0; i<N; i++) {
final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
@@ -4885,7 +4844,7 @@
mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
return false;
}
- num = addSharedLibraryLPw(file, num, changingLib);
+ addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
for (int i=0; i<N; i++) {
@@ -4895,13 +4854,12 @@
+ " desires unavailable shared library "
+ pkg.usesOptionalLibraries.get(i) + "; ignoring!");
} else {
- num = addSharedLibraryLPw(file, num, changingLib);
+ addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
}
- if (num > 0) {
- pkg.usesLibraryFiles = new String[num];
- System.arraycopy(mTmpSharedLibraries, 0,
- pkg.usesLibraryFiles, 0, num);
+ N = usesLibraryFiles.size();
+ if (N > 0) {
+ pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]);
} else {
pkg.usesLibraryFiles = null;
}
@@ -4947,7 +4905,7 @@
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
- File scanFile = new File(pkg.mScanPath);
+ final File scanFile = new File(pkg.codePath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
// Bail out. The resource and code paths haven't been set.
@@ -5385,7 +5343,7 @@
pkgSetting.uidError = uidError;
}
- String path = scanFile.getPath();
+ final String path = scanFile.getPath();
/* Note: We don't want to unpack the native binaries for
* system applications, unless they have been updated
* (the binaries are already under /system/lib).
@@ -5516,7 +5474,6 @@
handle.close();
}
}
- pkg.mScanPath = path;
if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
// We don't do this here during boot because we can do it all
@@ -5661,7 +5618,7 @@
synchronized (mPackages) {
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
- mAppDirs.put(pkg.mPath, pkg);
+ mAppDirs.put(pkg.codePath, pkg);
}
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
@@ -6282,8 +6239,8 @@
// writer
synchronized (mPackages) {
mPackages.remove(pkg.applicationInfo.packageName);
- if (pkg.mPath != null) {
- mAppDirs.remove(pkg.mPath);
+ if (pkg.codePath != null) {
+ mAppDirs.remove(pkg.codePath);
}
cleanPackageDataStructuresLILPw(pkg, chatty);
}
@@ -9850,7 +9807,7 @@
res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
return;
}
- if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
+ if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.codePath)) {
// Don't allow installation over an existing package with the same name.
Slog.w(TAG, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
@@ -9862,15 +9819,12 @@
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(newPackage,
- installerPackageName,
- null, null,
- res);
+ updateSettingsLI(null, newPackage, installerPackageName, null, null, res);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -9955,15 +9909,13 @@
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(newPackage,
- installerPackageName,
- allUsers, perUserInstalled,
- res);
+ updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
+ perUserInstalled, res);
updatedSettings = true;
}
}
@@ -9984,7 +9936,7 @@
// package that we deleted.
if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
- File restoreFile = new File(deletedPackage.mPath);
+ File restoreFile = new File(deletedPackage.codePath);
// Parse old package
boolean oldOnSd = isExternal(deletedPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
@@ -10069,7 +10021,7 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
@@ -10089,7 +10041,8 @@
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
+ updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
+ perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10115,12 +10068,21 @@
}
// Utility method used to move dex files during install.
- private int moveDexFilesLI(PackageParser.Package newPackage) {
- if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ private int moveDexFilesLI(PackageParser.Package oldPackage, PackageParser.Package newPackage) {
+ // TODO: extend to handle splits
+ if ((newPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
- int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
- instructionSet);
- if (retCode != 0) {
+
+ boolean moveSuccess = false;
+ if (oldPackage != null
+ && (oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ if (mInstaller.movedex(oldPackage.codePath, newPackage.codePath, instructionSet)
+ == 0) {
+ moveSuccess = true;
+ }
+ }
+
+ if (!moveSuccess) {
/*
* Programs may be lazily run through dexopt, so the
* source may not exist. However, something seems to
@@ -10129,17 +10091,19 @@
* remove the target to make sure there isn't a stale
* file from a previous version of the package.
*/
+ if (oldPackage != null) {
+ mInstaller.rmdex(oldPackage.codePath, instructionSet);
+ }
+ mInstaller.rmdex(newPackage.codePath, instructionSet);
newPackage.mDexOptNeeded = true;
- mInstaller.rmdex(newPackage.mScanPath, instructionSet);
- mInstaller.rmdex(newPackage.mPath, instructionSet);
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
- private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, boolean[] perUserInstalled,
- PackageInstalledInfo res) {
+ private void updateSettingsLI(PackageParser.Package oldPackage,
+ PackageParser.Package newPackage, String installerPackageName, int[] allUsers,
+ boolean[] perUserInstalled, PackageInstalledInfo res) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
@@ -10149,13 +10113,13 @@
mSettings.writeLPr();
}
- if ((res.returnCode = moveDexFilesLI(newPackage))
+ if ((res.returnCode = moveDexFilesLI(oldPackage, newPackage))
!= PackageManager.INSTALL_SUCCEEDED) {
// Discontinue if moving dex files failed.
return;
}
- if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath);
+ if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
updatePermissionsLPw(newPackage.packageName, newPackage,
@@ -10222,12 +10186,16 @@
| (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
PackageParser pp = new PackageParser(tmpPackageFile.getPath());
pp.setSeparateProcesses(mSeparateProcesses);
- final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
- null, mMetrics, parseFlags);
- if (pkg == null) {
- res.returnCode = pp.getParseError();
+
+ final PackageParser.Package pkg;
+ try {
+ pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics,
+ parseFlags);
+ } catch (PackageParserException e) {
+ res.returnCode = e.error;
return;
}
+
String pkgName = res.name = pkg.packageName;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
@@ -10235,8 +10203,11 @@
return;
}
}
- if (!pp.collectCertificates(pkg, parseFlags)) {
- res.returnCode = pp.getParseError();
+
+ try {
+ pp.collectCertificates(pkg, parseFlags);
+ } catch (PackageParserException e) {
+ res.returnCode = e.error;
return;
}
@@ -10341,7 +10312,9 @@
return;
}
// Set application objects path explicitly after the rename
- setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+ pkg.codePath = args.getCodePath();
+ pkg.applicationInfo.sourceDir = args.getCodePath();
+ pkg.applicationInfo.publicSourceDir = args.getResourcePath();
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
@@ -11221,7 +11194,7 @@
publicSrcDir = applicationInfo.publicSourceDir;
}
}
- int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath,
+ int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath,
publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps),
pStats);
if (res < 0) {
@@ -12854,7 +12827,7 @@
+ " Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
- final String oldCodePath = pkg.mPath;
+ final String oldCodePath = pkg.codePath;
final String newCodePath = mp.targetArgs.getCodePath();
final String newResPath = mp.targetArgs.getResourcePath();
final String newNativePath = mp.targetArgs
@@ -12884,18 +12857,20 @@
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mPath = newCodePath;
+ PackageParser.Package oldPackage = new PackageParser.Package(
+ pkg.packageName);
+ oldPackage.codePath = pkg.codePath;
+ pkg.codePath = newCodePath;
// Move dex files around
- if (moveDexFilesLI(pkg) != PackageManager.INSTALL_SUCCEEDED) {
+ if (moveDexFilesLI(oldPackage, pkg) != PackageManager.INSTALL_SUCCEEDED) {
// Moving of dex files failed. Set
// error code and abort move.
- pkg.mPath = pkg.mScanPath;
+ pkg.codePath = oldPackage.codePath;
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
}
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mScanPath = newCodePath;
pkg.applicationInfo.sourceDir = newCodePath;
pkg.applicationInfo.publicSourceDir = newResPath;
pkg.applicationInfo.nativeLibraryDir = newNativePath;
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
new file mode 100644
index 0000000..1da54e0
--- /dev/null
+++ b/telecomm/java/android/telecomm/PhoneApplication.java
@@ -0,0 +1,183 @@
+package android.telecomm;
+
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telecomm.ITelecommService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the primary phone application that will receive incoming calls, and be allowed
+ * to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class PhoneApplication {
+ private static final String TAG = PhoneApplication.class.getSimpleName();
+ private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
+ /**
+ * Sets the specified package name as the default phone application. The caller of this method
+ * needs to have permission to write to secure settings.
+ *
+ * @hide
+ * */
+ @SystemApi
+ public static void setDefaultPhoneApplication(String packageName, Context context) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<ComponentName> componentNames = getInstalledPhoneApplications(context);
+ ComponentName foundComponent = null;
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(componentName.getPackageName(), packageName)) {
+ foundComponent = componentName;
+ break;
+ }
+ }
+
+ if (foundComponent != null) {
+ // Update the secure setting.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
+ }
+ }
+
+ /**
+ * Returns the installed phone application that will be used to receive incoming calls, and is
+ * allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * @hide
+ * */
+ @SystemApi
+ public static ComponentName getDefaultPhoneApplication(Context context) {
+ String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION);
+
+ final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
+ if (!TextUtils.isEmpty(defaultPackageName)) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
+ return componentName;
+ }
+ }
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ ComponentName systemDialer = null;
+ try {
+ systemDialer = getTelecommService().getSystemPhoneApplication();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
+ return null;
+ }
+
+ if (systemDialer == null) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ // Verify that the system dialer has not been disabled.
+ return getComponentName(componentNames, systemDialer.getPackageName());
+ }
+
+ /**
+ * Returns a list of installed and available phone applications.
+ *
+ * In order to appear in the list, a phone application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ @SystemApi
+ public static List<ComponentName> getInstalledPhoneApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<ComponentName> componentNames = new ArrayList<ComponentName> ();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final ComponentName componentName =
+ new ComponentName(activityInfo.packageName, activityInfo.name);
+ componentNames.add(componentName);
+ }
+
+ // TODO: Filter for apps that don't handle DIAL intent with tel scheme
+ return componentNames;
+ }
+
+ /**
+ * Returns the {@link ComponentName} for the installed phone application for a given package
+ * name.
+ *
+ * @param context A valid context.
+ * @param packageName to retrieve the {@link ComponentName} for.
+ *
+ * @return The {@link ComponentName} for the installed phone application corresponding to the
+ * package name, or null if none is found.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static ComponentName getPhoneApplicationForPackageName(Context context,
+ String packageName) {
+ return getComponentName(getInstalledPhoneApplications(context), packageName);
+ }
+
+ /**
+ * Returns the component from a list of application components that corresponds to the package
+ * name.
+ *
+ * @param componentNames A list of component names
+ * @param packageName The package name to look for
+ * @return The {@link ComponentName} that matches the provided packageName, or null if not
+ * found.
+ */
+ private static ComponentName getComponentName(List<ComponentName> componentNames,
+ String packageName) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(packageName, componentName.getPackageName())) {
+ return componentName;
+ }
+ }
+ return null;
+ }
+
+ private static ITelecommService getTelecommService() {
+ return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+ }
+}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 8300c92..4ca878e 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -55,6 +55,21 @@
public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
/**
+ * Activity action: Ask the user to change the default phone application. This will show a
+ * dialog that asks the user whether they want to replace the current default phone application
+ * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
+ */
+ public static final String ACTION_CHANGE_DEFAULT_PHONE =
+ "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
+
+ /**
+ * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
+ *
+ * @see #ACTION_CHANGE_DEFAULT_PHONE
+ */
+ public static final String EXTRA_PACKAGE_NAME = "package";
+
+ /**
* Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
* describes the call service to use for the incoming call.
*/
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
new file mode 100644
index 0000000..a97e7e4
--- /dev/null
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telecomm;
+
+import android.content.Context;
+
+import com.android.internal.telecomm.ITelecommService;
+
+/**
+ * Provides access to Telecomm-related functionality.
+ */
+public class TelecommManager {
+ private static final String TAG = "TelecommManager";
+
+ private final Context mContext;
+ private final ITelecommService mService;
+
+ /** @hide */
+ public TelecommManager(Context context, ITelecommService service) {
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+
+ mService = service;
+ }
+
+ /** {@hide} */
+ public static TelecommManager from(Context context) {
+ return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 0e94ffb..638b86a 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,6 +16,8 @@
package com.android.internal.telecomm;
+import android.content.ComponentName;
+
/**
* Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
* commands that were previously handled by ITelephony.
@@ -38,4 +40,9 @@
* @param showDialpad if true, make the dialpad visible initially.
*/
void showCallScreen(boolean showDialpad);
+
+ /**
+ * Returns the component name of the phone application installed on the system partition.
+ */
+ ComponentName getSystemPhoneApplication();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index aaee99f..50bbb1e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1414,6 +1414,9 @@
// FIXME the argument to pass is subId ??
public int getSimState(int slotId) {
long[] subId = SubscriptionManager.getSubId(slotId);
+ if (subId == null) {
+ return SIM_STATE_ABSENT;
+ }
// FIXME Do not use a property to determine SIM_STATE, call
// appropriate method on some object.
String prop =
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 54ac71e..33ccad5 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -493,7 +493,8 @@
}
if (this.mType.equals("TLS")) {
result = this.mRealm.equals(other.mRealm) &&
- this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
+ this.mHomeSpFqdn.equals(other.mHomeSpFqdn) &&
+ this.mClientCert.equals(other.mClientCert);
}
if (this.mType.equals("SIM")) {
result = this.mMcc.equals(other.mMcc) &&
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
index 9fccf0a..f84ac88 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
@@ -16,10 +16,18 @@
package android.net.wifi.passpoint;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.ScanResult;
import android.os.Parcelable;
import android.os.Parcel;
+import android.security.Credentials;
import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+
/** @hide */
public class WifiPasspointPolicy implements Parcelable {
@@ -43,6 +51,20 @@
private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
private boolean mIsHomeSp;
+ private final String INT_PRIVATE_KEY = "private_key";
+ private final String INT_PHASE2 = "phase2";
+ private final String INT_PASSWORD = "password";
+ private final String INT_IDENTITY = "identity";
+ private final String INT_EAP = "eap";
+ private final String INT_CLIENT_CERT = "client_cert";
+ private final String INT_CA_CERT = "ca_cert";
+ private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
+ private final String INT_SIM_SLOT = "sim_slot";
+ private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
+ private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
+ private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
+
/** @hide */
public WifiPasspointPolicy(String name, String ssid,
String bssid, WifiPasspointCredential pc,
@@ -89,7 +111,7 @@
}
/** @hide */
- public boolean getHomeSp() {
+ public boolean isHomeSp() {
return mIsHomeSp;
}
@@ -121,6 +143,142 @@
return mRoamingPriority;
}
+ public WifiConfiguration createWifiConfiguration() {
+ WifiConfiguration wfg = new WifiConfiguration();
+ if (mBssid != null) {
+ Log.d(TAG, "create bssid:" + mBssid);
+ wfg.BSSID = mBssid;
+ }
+
+ if (mSsid != null) {
+ Log.d(TAG, "create ssid:" + mSsid);
+ wfg.SSID = mSsid;
+ }
+ //TODO: 1. add pmf configuration
+ // 2. add ocsp configuration
+ // 3. add eap-sim configuration
+ /*Key management*/
+ wfg.status = WifiConfiguration.Status.ENABLED;
+ wfg.allowedKeyManagement.clear();
+ wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+
+ /*Group Ciphers*/
+ wfg.allowedGroupCiphers.clear();
+ wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+
+ /*Protocols*/
+ wfg.allowedProtocols.clear();
+ wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+
+ Class[] enterpriseFieldArray = WifiConfiguration.class.getClasses();
+ Class<?> enterpriseFieldClass = null;
+
+
+ for(Class<?> myClass : enterpriseFieldArray) {
+ if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
+ enterpriseFieldClass = myClass;
+ break;
+ }
+ }
+ Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
+
+
+ Field anonymousId = null, caCert = null, clientCert = null,
+ eap = null, identity = null, password = null,
+ phase2 = null, privateKey = null;
+
+ Field[] fields = WifiConfiguration.class.getFields();
+
+
+ for (Field tempField : fields) {
+ if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
+ anonymousId = tempField;
+ Log.d(TAG, "field " + anonymousId.getName() );
+ } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
+ caCert = tempField;
+ } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
+ clientCert = tempField;
+ Log.d(TAG, "field " + clientCert.getName() );
+ } else if (tempField.getName().trim().equals(INT_EAP)) {
+ eap = tempField;
+ Log.d(TAG, "field " + eap.getName() );
+ } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
+ identity = tempField;
+ Log.d(TAG, "field " + identity.getName() );
+ } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
+ password = tempField;
+ Log.d(TAG, "field " + password.getName() );
+ } else if (tempField.getName().trim().equals(INT_PHASE2)) {
+ phase2 = tempField;
+ Log.d(TAG, "field " + phase2.getName() );
+
+ } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
+ privateKey = tempField;
+ }
+ }
+
+
+ Method setValue = null;
+
+ for(Method m: enterpriseFieldClass.getMethods()) {
+ if(m.getName().trim().equals("setValue")) {
+ Log.d(TAG, "method " + m.getName() );
+ setValue = m;
+ break;
+ }
+ }
+
+ try {
+ // EAP
+ String eapmethod = mCredential.getType();
+ Log.d(TAG, "eapmethod:" + eapmethod);
+ setValue.invoke(eap.get(wfg), eapmethod);
+
+ // Username, password, EAP Phase 2
+ if ("TTLS".equals(eapmethod)) {
+ setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
+ setValue.invoke(identity.get(wfg), mCredential.getUserName());
+ setValue.invoke(password.get(wfg), mCredential.getPassword());
+ setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
+ }
+
+ // EAP CA Certificate
+ String cacertificate = null;
+ String rootCA = mCredential.getCaRootCertPath();
+ if (rootCA == null){
+ cacertificate = null;
+ } else {
+ cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
+ }
+ Log.d(TAG, "cacertificate:" + cacertificate);
+ setValue.invoke(caCert.get(wfg), cacertificate);
+
+ //User certificate
+ if ("TLS".equals(eapmethod)) {
+ String usercertificate = null;
+ String privatekey = null;
+ String clientCertPath = mCredential.getClientCertPath();
+ if (clientCertPath != null){
+ privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
+ usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
+ }
+ Log.d(TAG, "privatekey:" + privatekey);
+ Log.d(TAG, "usercertificate:" + usercertificate);
+ if (privatekey != null && usercertificate != null) {
+ setValue.invoke(privateKey.get(wfg), privatekey);
+ setValue.invoke(clientCert.get(wfg), usercertificate);
+ }
+ }
+ } catch (Exception e) {
+ Log.d(TAG, "createWifiConfiguration err:" + e);
+ }
+
+ return wfg;
+ }
+
/** {@inheritDoc} @hide */
public int compareTo(WifiPasspointPolicy another) {
Log.d(TAG, "this:" + this);
@@ -128,11 +286,11 @@
if (another == null) {
return -1;
- } else if (this.mIsHomeSp == true && another.getHomeSp() == false) {
+ } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
//home sp priority is higher then roaming
Log.d(TAG, "compare HomeSP first, this is HomeSP, another isn't");
return -1;
- } else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) {
+ } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
Log.d(TAG, "both HomeSP");
//if both home sp, compare credential priority
if (this.mCredentialPriority < another.getCredentialPriority()) {
@@ -160,7 +318,7 @@
} else {
return 1;
}
- } else if ((this.mIsHomeSp == false && another.getHomeSp() == false)) {
+ } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
Log.d(TAG, "both RoamingSp");
//if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
if (this.mRoamingPriority < another.getRoamingPriority()) {