diff options
423 files changed, 12347 insertions, 5176 deletions
@@ -76,8 +76,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStopUserCallback.aidl \ - core/java/android/app/task/ITaskCallback.aidl \ - core/java/android/app/task/ITaskService.aidl \ + core/java/android/app/task/ITaskCallback.aidl \ + core/java/android/app/task/ITaskService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiAutomationConnection.aidl \ diff --git a/api/current.txt b/api/current.txt index edc8700..5a22eb1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -567,6 +567,7 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 + field public static final int fromId = 16843856; // 0x1010450 field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 field public static final int fromXScale = 16843202; // 0x10101c2 @@ -956,6 +957,7 @@ package android { field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d field public static final int restrictedAccountType = 16843733; // 0x10103d5 + field public static final int reversible = 16843857; // 0x1010451 field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -1045,6 +1047,7 @@ package android { field public static final int spinnerStyle = 16842881; // 0x1010081 field public static final int spinnersShown = 16843595; // 0x101034b field public static final int splitMotionEvents = 16843503; // 0x10102ef + field public static final int splitTrack = 16843858; // 0x1010452 field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 field public static final int sspPattern = 16843749; // 0x10103e5 @@ -1214,6 +1217,7 @@ package android { field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 + field public static final int toId = 16843855; // 0x101044f field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 field public static final int toXScale = 16843203; // 0x10101c3 @@ -3258,6 +3262,7 @@ package android.app { method public boolean onContextItemSelected(android.view.MenuItem); method public void onContextMenuClosed(android.view.Menu); method protected void onCreate(android.os.Bundle); + method protected void onCreate(android.os.Bundle, android.os.PersistableBundle); method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo); method public java.lang.CharSequence onCreateDescription(); method protected deprecated android.app.Dialog onCreateDialog(int); @@ -3288,6 +3293,7 @@ package android.app { method public void onPanelClosed(int, android.view.Menu); method protected void onPause(); method protected void onPostCreate(android.os.Bundle); + method protected void onPostCreate(android.os.Bundle, android.os.PersistableBundle); method protected void onPostResume(); method protected deprecated void onPrepareDialog(int, android.app.Dialog); method protected deprecated void onPrepareDialog(int, android.app.Dialog, android.os.Bundle); @@ -3297,9 +3303,11 @@ package android.app { method public void onProvideAssistData(android.os.Bundle); method protected void onRestart(); method protected void onRestoreInstanceState(android.os.Bundle); + method protected void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle); method protected void onResume(); method public deprecated java.lang.Object onRetainNonConfigurationInstance(); method protected void onSaveInstanceState(android.os.Bundle); + method protected void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle); method public boolean onSearchRequested(); method protected void onStart(); method protected void onStop(); @@ -4195,14 +4203,18 @@ package android.app { method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean); method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle); + method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnDestroy(android.app.Activity); method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent); method public void callActivityOnPause(android.app.Activity); method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle); + method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnRestart(android.app.Activity); method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle); + method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnResume(android.app.Activity); method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle); + method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnStart(android.app.Activity); method public void callActivityOnStop(android.app.Activity); method public void callActivityOnUserLeaving(android.app.Activity); @@ -4838,6 +4850,7 @@ package android.app { method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; + method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); @@ -5050,6 +5063,7 @@ package android.app.admin { method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isDeviceOwnerApp(java.lang.String); + method public boolean isLockTaskPermitted(android.content.ComponentName); method public boolean isProfileOwnerApp(java.lang.String); method public void lockNow(); method public void removeActiveAdmin(android.content.ComponentName); @@ -5058,6 +5072,7 @@ package android.app.admin { method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); + method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException; method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int); method public void setMaximumTimeToLock(android.content.ComponentName, long); method public void setPasswordExpirationTimeout(android.content.ComponentName, long); @@ -8047,6 +8062,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera"; field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus"; + field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash"; field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front"; field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; @@ -10819,6 +10835,12 @@ package android.graphics.drawable { method public abstract void stop(); } + public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable { + ctor public AnimatedStateListDrawable(); + method public void addState(int[], android.graphics.drawable.Drawable, int); + method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean); + } + public class AnimationDrawable extends android.graphics.drawable.DrawableContainer implements android.graphics.drawable.Animatable java.lang.Runnable { ctor public AnimationDrawable(); method public void addFrame(android.graphics.drawable.Drawable, int); @@ -12867,6 +12889,7 @@ package android.inputmethodservice { method public void onStartInputView(android.view.inputmethod.EditorInfo, boolean); method public void onUnbindInput(); method public void onUpdateCursor(android.graphics.Rect); + method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo); method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText); method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo); method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo); @@ -12916,6 +12939,7 @@ package android.inputmethodservice { method public void finishInput(); method public void toggleSoftInput(int, int); method public void updateCursor(android.graphics.Rect); + method public void updateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo); method public void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public void updateSelection(int, int, int, int, int, int); method public void viewClicked(boolean); @@ -13317,6 +13341,45 @@ package android.media { method public void stop(); } + public final class AudioAttributes { + method public int getContentType(); + method public int getFlags(); + method public java.util.Set<java.lang.String> getTags(); + method public int getUsage(); + field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 + field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 + field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4 + field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1 + field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0 + field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 + field public static final int USAGE_ALARM = 4; // 0x4 + field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb + field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc + field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd + field public static final int USAGE_GAME = 14; // 0xe + field public static final int USAGE_MEDIA = 1; // 0x1 + field public static final int USAGE_NOTIFICATION = 5; // 0x5 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7 + field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa + field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6 + field public static final int USAGE_UNKNOWN = 0; // 0x0 + field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2 + field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3 + } + + public static class AudioAttributes.Builder { + ctor public AudioAttributes.Builder(); + ctor public AudioAttributes.Builder(android.media.AudioAttributes); + method public android.media.AudioAttributes.Builder addTag(java.lang.String); + method public android.media.AudioAttributes build(); + method public android.media.AudioAttributes.Builder setContentType(int); + method public android.media.AudioAttributes.Builder setFlags(int); + method public android.media.AudioAttributes.Builder setLegacyStreamType(int); + method public android.media.AudioAttributes.Builder setUsage(int); + } + public class AudioFormat { ctor public AudioFormat(); field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1 @@ -13361,6 +13424,7 @@ package android.media { field public static final int ENCODING_INVALID = 0; // 0x0 field public static final int ENCODING_PCM_16BIT = 2; // 0x2 field public static final int ENCODING_PCM_8BIT = 3; // 0x3 + field public static final int ENCODING_PCM_FLOAT = 4; // 0x4 } public class AudioManager { @@ -13578,6 +13642,7 @@ package android.media { method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(short[], int, int); + method public int write(float[], int, int, int); method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe @@ -13810,6 +13875,7 @@ package android.media { method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; method public final void release(); method public final void releaseOutputBuffer(int, boolean); + method public final void releaseOutputBuffer(int, long); method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback); method public final void setParameters(android.os.Bundle); method public final void setVideoScalingMode(int); @@ -17133,6 +17199,7 @@ package android.nfc.cardemulation { } public final class CardEmulation { + method public boolean categoryAllowsForegroundPreference(java.lang.String); method public android.nfc.cardemulation.AidGroup getAidGroupForService(android.content.ComponentName, java.lang.String); method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); method public int getSelectionModeForCategory(java.lang.String); @@ -17140,6 +17207,8 @@ package android.nfc.cardemulation { method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String); method public boolean registerAidGroupForService(android.content.ComponentName, android.nfc.cardemulation.AidGroup); method public boolean removeAidGroupForService(android.content.ComponentName, java.lang.String); + method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); + method public boolean unsetPreferredService(android.app.Activity); field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; field public static final java.lang.String CATEGORY_OTHER = "other"; field public static final java.lang.String CATEGORY_PAYMENT = "payment"; @@ -25153,16 +25222,24 @@ package android.service.notification { method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int); method public final void cancelNotification(java.lang.String); method public final void cancelNotifications(java.lang.String[]); - method public java.lang.String[] getActiveNotificationKeys(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]); + method public java.lang.String[] getOrderedNotificationKeys(); method public android.os.IBinder onBind(android.content.Intent); method public void onListenerConnected(java.lang.String[]); + method public void onNotificationOrderUpdate(); method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification); method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; } + public class NotificationOrderUpdate implements android.os.Parcelable { + ctor public NotificationOrderUpdate(android.os.Parcel); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public class StatusBarNotification implements android.os.Parcelable { ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); ctor public StatusBarNotification(android.os.Parcel); @@ -25716,6 +25793,637 @@ package android.speech.tts { } +package android.system { + + public final class ErrnoException extends java.lang.Exception { + ctor public ErrnoException(java.lang.String, int); + ctor public ErrnoException(java.lang.String, int, java.lang.Throwable); + field public final int errno; + } + + public final class Os { + method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static boolean access(java.lang.String, int) throws android.system.ErrnoException; + method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static void chmod(java.lang.String, int) throws android.system.ErrnoException; + method public static void chown(java.lang.String, int, int) throws android.system.ErrnoException; + method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static void connect(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static java.io.FileDescriptor dup(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static java.io.FileDescriptor dup2(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static java.lang.String[] environ(); + method public static void execv(java.lang.String, java.lang.String[]) throws android.system.ErrnoException; + method public static void execve(java.lang.String, java.lang.String[], java.lang.String[]) throws android.system.ErrnoException; + method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException; + method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static void fsync(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static void ftruncate(java.io.FileDescriptor, long) throws android.system.ErrnoException; + method public static java.lang.String gai_strerror(int); + method public static int getegid(); + method public static java.lang.String getenv(java.lang.String); + method public static int geteuid(); + method public static int getgid(); + method public static java.net.SocketAddress getpeername(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static int getpid(); + method public static int getppid(); + method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static int gettid(); + method public static int getuid(); + method public static java.lang.String if_indextoname(int); + method public static java.net.InetAddress inet_pton(int, java.lang.String); + method public static boolean isatty(java.io.FileDescriptor); + method public static void kill(int, int) throws android.system.ErrnoException; + method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException; + method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException; + method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException; + method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException; + method public static void mincore(long, long, byte[]) throws android.system.ErrnoException; + method public static void mkdir(java.lang.String, int) throws android.system.ErrnoException; + method public static void mkfifo(java.lang.String, int) throws android.system.ErrnoException; + method public static void mlock(long, long) throws android.system.ErrnoException; + method public static long mmap(long, long, int, int, java.io.FileDescriptor, long) throws android.system.ErrnoException; + method public static void msync(long, long, int) throws android.system.ErrnoException; + method public static void munlock(long, long) throws android.system.ErrnoException; + method public static void munmap(long, long) throws android.system.ErrnoException; + method public static java.io.FileDescriptor open(java.lang.String, int, int) throws android.system.ErrnoException; + method public static java.io.FileDescriptor[] pipe() throws android.system.ErrnoException; + method public static int poll(android.system.StructPollfd[], int) throws android.system.ErrnoException; + method public static void posix_fallocate(java.io.FileDescriptor, long, long) throws android.system.ErrnoException; + method public static int prctl(int, long, long, long, long) throws android.system.ErrnoException; + method public static int pread(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int pread(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int pwrite(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int pwrite(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int read(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int read(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static java.lang.String readlink(java.lang.String) throws android.system.ErrnoException; + method public static int readv(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void remove(java.lang.String) throws android.system.ErrnoException; + method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException; + method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException; + method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static void setegid(int) throws android.system.ErrnoException; + method public static void setenv(java.lang.String, java.lang.String, boolean) throws android.system.ErrnoException; + method public static void seteuid(int) throws android.system.ErrnoException; + method public static void setgid(int) throws android.system.ErrnoException; + method public static int setsid() throws android.system.ErrnoException; + method public static void setuid(int) throws android.system.ErrnoException; + method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException; + method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException; + method public static android.system.StructStat stat(java.lang.String) throws android.system.ErrnoException; + method public static android.system.StructStatVfs statvfs(java.lang.String) throws android.system.ErrnoException; + method public static java.lang.String strerror(int); + method public static java.lang.String strsignal(int); + method public static void symlink(java.lang.String, java.lang.String) throws android.system.ErrnoException; + method public static long sysconf(int); + method public static void tcdrain(java.io.FileDescriptor) throws android.system.ErrnoException; + method public static void tcsendbreak(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static int umask(int); + method public static android.system.StructUtsname uname(); + method public static void unsetenv(java.lang.String) throws android.system.ErrnoException; + method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException; + method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException; + method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException; + } + + public final class OsConstants { + method public static boolean S_ISBLK(int); + method public static boolean S_ISCHR(int); + method public static boolean S_ISDIR(int); + method public static boolean S_ISFIFO(int); + method public static boolean S_ISLNK(int); + method public static boolean S_ISREG(int); + method public static boolean S_ISSOCK(int); + method public static boolean WCOREDUMP(int); + method public static int WEXITSTATUS(int); + method public static boolean WIFEXITED(int); + method public static boolean WIFSIGNALED(int); + method public static boolean WIFSTOPPED(int); + method public static int WSTOPSIG(int); + method public static int WTERMSIG(int); + method public static java.lang.String errnoName(int); + method public static java.lang.String gaiName(int); + field public static final int AF_INET; + field public static final int AF_INET6; + field public static final int AF_UNIX; + field public static final int AF_UNSPEC; + field public static final int AI_ADDRCONFIG; + field public static final int AI_ALL; + field public static final int AI_CANONNAME; + field public static final int AI_NUMERICHOST; + field public static final int AI_NUMERICSERV; + field public static final int AI_PASSIVE; + field public static final int AI_V4MAPPED; + field public static final int CAP_AUDIT_CONTROL; + field public static final int CAP_AUDIT_WRITE; + field public static final int CAP_BLOCK_SUSPEND; + field public static final int CAP_CHOWN; + field public static final int CAP_DAC_OVERRIDE; + field public static final int CAP_DAC_READ_SEARCH; + field public static final int CAP_FOWNER; + field public static final int CAP_FSETID; + field public static final int CAP_IPC_LOCK; + field public static final int CAP_IPC_OWNER; + field public static final int CAP_KILL; + field public static final int CAP_LAST_CAP; + field public static final int CAP_LEASE; + field public static final int CAP_LINUX_IMMUTABLE; + field public static final int CAP_MAC_ADMIN; + field public static final int CAP_MAC_OVERRIDE; + field public static final int CAP_MKNOD; + field public static final int CAP_NET_ADMIN; + field public static final int CAP_NET_BIND_SERVICE; + field public static final int CAP_NET_BROADCAST; + field public static final int CAP_NET_RAW; + field public static final int CAP_SETFCAP; + field public static final int CAP_SETGID; + field public static final int CAP_SETPCAP; + field public static final int CAP_SETUID; + field public static final int CAP_SYSLOG; + field public static final int CAP_SYS_ADMIN; + field public static final int CAP_SYS_BOOT; + field public static final int CAP_SYS_CHROOT; + field public static final int CAP_SYS_MODULE; + field public static final int CAP_SYS_NICE; + field public static final int CAP_SYS_PACCT; + field public static final int CAP_SYS_PTRACE; + field public static final int CAP_SYS_RAWIO; + field public static final int CAP_SYS_RESOURCE; + field public static final int CAP_SYS_TIME; + field public static final int CAP_SYS_TTY_CONFIG; + field public static final int CAP_WAKE_ALARM; + field public static final int E2BIG; + field public static final int EACCES; + field public static final int EADDRINUSE; + field public static final int EADDRNOTAVAIL; + field public static final int EAFNOSUPPORT; + field public static final int EAGAIN; + field public static final int EAI_AGAIN; + field public static final int EAI_BADFLAGS; + field public static final int EAI_FAIL; + field public static final int EAI_FAMILY; + field public static final int EAI_MEMORY; + field public static final int EAI_NODATA; + field public static final int EAI_NONAME; + field public static final int EAI_OVERFLOW; + field public static final int EAI_SERVICE; + field public static final int EAI_SOCKTYPE; + field public static final int EAI_SYSTEM; + field public static final int EALREADY; + field public static final int EBADF; + field public static final int EBADMSG; + field public static final int EBUSY; + field public static final int ECANCELED; + field public static final int ECHILD; + field public static final int ECONNABORTED; + field public static final int ECONNREFUSED; + field public static final int ECONNRESET; + field public static final int EDEADLK; + field public static final int EDESTADDRREQ; + field public static final int EDOM; + field public static final int EDQUOT; + field public static final int EEXIST; + field public static final int EFAULT; + field public static final int EFBIG; + field public static final int EHOSTUNREACH; + field public static final int EIDRM; + field public static final int EILSEQ; + field public static final int EINPROGRESS; + field public static final int EINTR; + field public static final int EINVAL; + field public static final int EIO; + field public static final int EISCONN; + field public static final int EISDIR; + field public static final int ELOOP; + field public static final int EMFILE; + field public static final int EMLINK; + field public static final int EMSGSIZE; + field public static final int EMULTIHOP; + field public static final int ENAMETOOLONG; + field public static final int ENETDOWN; + field public static final int ENETRESET; + field public static final int ENETUNREACH; + field public static final int ENFILE; + field public static final int ENOBUFS; + field public static final int ENODATA; + field public static final int ENODEV; + field public static final int ENOENT; + field public static final int ENOEXEC; + field public static final int ENOLCK; + field public static final int ENOLINK; + field public static final int ENOMEM; + field public static final int ENOMSG; + field public static final int ENOPROTOOPT; + field public static final int ENOSPC; + field public static final int ENOSR; + field public static final int ENOSTR; + field public static final int ENOSYS; + field public static final int ENOTCONN; + field public static final int ENOTDIR; + field public static final int ENOTEMPTY; + field public static final int ENOTSOCK; + field public static final int ENOTSUP; + field public static final int ENOTTY; + field public static final int ENXIO; + field public static final int EOPNOTSUPP; + field public static final int EOVERFLOW; + field public static final int EPERM; + field public static final int EPIPE; + field public static final int EPROTO; + field public static final int EPROTONOSUPPORT; + field public static final int EPROTOTYPE; + field public static final int ERANGE; + field public static final int EROFS; + field public static final int ESPIPE; + field public static final int ESRCH; + field public static final int ESTALE; + field public static final int ETIME; + field public static final int ETIMEDOUT; + field public static final int ETXTBSY; + field public static final int EXDEV; + field public static final int EXIT_FAILURE; + field public static final int EXIT_SUCCESS; + field public static final int FD_CLOEXEC; + field public static final int FIONREAD; + field public static final int F_DUPFD; + field public static final int F_GETFD; + field public static final int F_GETFL; + field public static final int F_GETLK; + field public static final int F_GETLK64; + field public static final int F_GETOWN; + field public static final int F_OK; + field public static final int F_RDLCK; + field public static final int F_SETFD; + field public static final int F_SETFL; + field public static final int F_SETLK; + field public static final int F_SETLK64; + field public static final int F_SETLKW; + field public static final int F_SETLKW64; + field public static final int F_SETOWN; + field public static final int F_UNLCK; + field public static final int F_WRLCK; + field public static final int IFA_F_DADFAILED; + field public static final int IFA_F_DEPRECATED; + field public static final int IFA_F_HOMEADDRESS; + field public static final int IFA_F_NODAD; + field public static final int IFA_F_OPTIMISTIC; + field public static final int IFA_F_PERMANENT; + field public static final int IFA_F_SECONDARY; + field public static final int IFA_F_TEMPORARY; + field public static final int IFA_F_TENTATIVE; + field public static final int IFF_ALLMULTI; + field public static final int IFF_AUTOMEDIA; + field public static final int IFF_BROADCAST; + field public static final int IFF_DEBUG; + field public static final int IFF_DYNAMIC; + field public static final int IFF_LOOPBACK; + field public static final int IFF_MASTER; + field public static final int IFF_MULTICAST; + field public static final int IFF_NOARP; + field public static final int IFF_NOTRAILERS; + field public static final int IFF_POINTOPOINT; + field public static final int IFF_PORTSEL; + field public static final int IFF_PROMISC; + field public static final int IFF_RUNNING; + field public static final int IFF_SLAVE; + field public static final int IFF_UP; + field public static final int IPPROTO_ICMP; + field public static final int IPPROTO_ICMPV6; + field public static final int IPPROTO_IP; + field public static final int IPPROTO_IPV6; + field public static final int IPPROTO_RAW; + field public static final int IPPROTO_TCP; + field public static final int IPPROTO_UDP; + field public static final int IPV6_CHECKSUM; + field public static final int IPV6_MULTICAST_HOPS; + field public static final int IPV6_MULTICAST_IF; + field public static final int IPV6_MULTICAST_LOOP; + field public static final int IPV6_RECVDSTOPTS; + field public static final int IPV6_RECVHOPLIMIT; + field public static final int IPV6_RECVHOPOPTS; + field public static final int IPV6_RECVPKTINFO; + field public static final int IPV6_RECVRTHDR; + field public static final int IPV6_RECVTCLASS; + field public static final int IPV6_TCLASS; + field public static final int IPV6_UNICAST_HOPS; + field public static final int IPV6_V6ONLY; + field public static final int IP_MULTICAST_IF; + field public static final int IP_MULTICAST_LOOP; + field public static final int IP_MULTICAST_TTL; + field public static final int IP_TOS; + field public static final int IP_TTL; + field public static final int MAP_FIXED; + field public static final int MAP_PRIVATE; + field public static final int MAP_SHARED; + field public static final int MCAST_BLOCK_SOURCE; + field public static final int MCAST_JOIN_GROUP; + field public static final int MCAST_JOIN_SOURCE_GROUP; + field public static final int MCAST_LEAVE_GROUP; + field public static final int MCAST_LEAVE_SOURCE_GROUP; + field public static final int MCAST_UNBLOCK_SOURCE; + field public static final int MCL_CURRENT; + field public static final int MCL_FUTURE; + field public static final int MSG_CTRUNC; + field public static final int MSG_DONTROUTE; + field public static final int MSG_EOR; + field public static final int MSG_OOB; + field public static final int MSG_PEEK; + field public static final int MSG_TRUNC; + field public static final int MSG_WAITALL; + field public static final int MS_ASYNC; + field public static final int MS_INVALIDATE; + field public static final int MS_SYNC; + field public static final int NI_DGRAM; + field public static final int NI_NAMEREQD; + field public static final int NI_NOFQDN; + field public static final int NI_NUMERICHOST; + field public static final int NI_NUMERICSERV; + field public static final int O_ACCMODE; + field public static final int O_APPEND; + field public static final int O_CREAT; + field public static final int O_EXCL; + field public static final int O_NOCTTY; + field public static final int O_NOFOLLOW; + field public static final int O_NONBLOCK; + field public static final int O_RDONLY; + field public static final int O_RDWR; + field public static final int O_SYNC; + field public static final int O_TRUNC; + field public static final int O_WRONLY; + field public static final int POLLERR; + field public static final int POLLHUP; + field public static final int POLLIN; + field public static final int POLLNVAL; + field public static final int POLLOUT; + field public static final int POLLPRI; + field public static final int POLLRDBAND; + field public static final int POLLRDNORM; + field public static final int POLLWRBAND; + field public static final int POLLWRNORM; + field public static final int PROT_EXEC; + field public static final int PROT_NONE; + field public static final int PROT_READ; + field public static final int PROT_WRITE; + field public static final int PR_SET_NO_NEW_PRIVS; + field public static final int RT_SCOPE_HOST; + field public static final int RT_SCOPE_LINK; + field public static final int RT_SCOPE_NOWHERE; + field public static final int RT_SCOPE_SITE; + field public static final int RT_SCOPE_UNIVERSE; + field public static final int R_OK; + field public static final int SEEK_CUR; + field public static final int SEEK_END; + field public static final int SEEK_SET; + field public static final int SHUT_RD; + field public static final int SHUT_RDWR; + field public static final int SHUT_WR; + field public static final int SIGABRT; + field public static final int SIGALRM; + field public static final int SIGBUS; + field public static final int SIGCHLD; + field public static final int SIGCONT; + field public static final int SIGFPE; + field public static final int SIGHUP; + field public static final int SIGILL; + field public static final int SIGINT; + field public static final int SIGIO; + field public static final int SIGKILL; + field public static final int SIGPIPE; + field public static final int SIGPROF; + field public static final int SIGPWR; + field public static final int SIGQUIT; + field public static final int SIGRTMAX; + field public static final int SIGRTMIN; + field public static final int SIGSEGV; + field public static final int SIGSTKFLT; + field public static final int SIGSTOP; + field public static final int SIGSYS; + field public static final int SIGTERM; + field public static final int SIGTRAP; + field public static final int SIGTSTP; + field public static final int SIGTTIN; + field public static final int SIGTTOU; + field public static final int SIGURG; + field public static final int SIGUSR1; + field public static final int SIGUSR2; + field public static final int SIGVTALRM; + field public static final int SIGWINCH; + field public static final int SIGXCPU; + field public static final int SIGXFSZ; + field public static final int SIOCGIFADDR; + field public static final int SIOCGIFBRDADDR; + field public static final int SIOCGIFDSTADDR; + field public static final int SIOCGIFNETMASK; + field public static final int SOCK_DGRAM; + field public static final int SOCK_RAW; + field public static final int SOCK_SEQPACKET; + field public static final int SOCK_STREAM; + field public static final int SOL_SOCKET; + field public static final int SO_BINDTODEVICE; + field public static final int SO_BROADCAST; + field public static final int SO_DEBUG; + field public static final int SO_DONTROUTE; + field public static final int SO_ERROR; + field public static final int SO_KEEPALIVE; + field public static final int SO_LINGER; + field public static final int SO_OOBINLINE; + field public static final int SO_PASSCRED; + field public static final int SO_PEERCRED; + field public static final int SO_RCVBUF; + field public static final int SO_RCVLOWAT; + field public static final int SO_RCVTIMEO; + field public static final int SO_REUSEADDR; + field public static final int SO_SNDBUF; + field public static final int SO_SNDLOWAT; + field public static final int SO_SNDTIMEO; + field public static final int SO_TYPE; + field public static final int STDERR_FILENO; + field public static final int STDIN_FILENO; + field public static final int STDOUT_FILENO; + field public static final int S_IFBLK; + field public static final int S_IFCHR; + field public static final int S_IFDIR; + field public static final int S_IFIFO; + field public static final int S_IFLNK; + field public static final int S_IFMT; + field public static final int S_IFREG; + field public static final int S_IFSOCK; + field public static final int S_IRGRP; + field public static final int S_IROTH; + field public static final int S_IRUSR; + field public static final int S_IRWXG; + field public static final int S_IRWXO; + field public static final int S_IRWXU; + field public static final int S_ISGID; + field public static final int S_ISUID; + field public static final int S_ISVTX; + field public static final int S_IWGRP; + field public static final int S_IWOTH; + field public static final int S_IWUSR; + field public static final int S_IXGRP; + field public static final int S_IXOTH; + field public static final int S_IXUSR; + field public static final int TCP_NODELAY; + field public static final int WCONTINUED; + field public static final int WEXITED; + field public static final int WNOHANG; + field public static final int WNOWAIT; + field public static final int WSTOPPED; + field public static final int WUNTRACED; + field public static final int W_OK; + field public static final int X_OK; + field public static final int _SC_2_CHAR_TERM; + field public static final int _SC_2_C_BIND; + field public static final int _SC_2_C_DEV; + field public static final int _SC_2_C_VERSION; + field public static final int _SC_2_FORT_DEV; + field public static final int _SC_2_FORT_RUN; + field public static final int _SC_2_LOCALEDEF; + field public static final int _SC_2_SW_DEV; + field public static final int _SC_2_UPE; + field public static final int _SC_2_VERSION; + field public static final int _SC_AIO_LISTIO_MAX; + field public static final int _SC_AIO_MAX; + field public static final int _SC_AIO_PRIO_DELTA_MAX; + field public static final int _SC_ARG_MAX; + field public static final int _SC_ASYNCHRONOUS_IO; + field public static final int _SC_ATEXIT_MAX; + field public static final int _SC_AVPHYS_PAGES; + field public static final int _SC_BC_BASE_MAX; + field public static final int _SC_BC_DIM_MAX; + field public static final int _SC_BC_SCALE_MAX; + field public static final int _SC_BC_STRING_MAX; + field public static final int _SC_CHILD_MAX; + field public static final int _SC_CLK_TCK; + field public static final int _SC_COLL_WEIGHTS_MAX; + field public static final int _SC_DELAYTIMER_MAX; + field public static final int _SC_EXPR_NEST_MAX; + field public static final int _SC_FSYNC; + field public static final int _SC_GETGR_R_SIZE_MAX; + field public static final int _SC_GETPW_R_SIZE_MAX; + field public static final int _SC_IOV_MAX; + field public static final int _SC_JOB_CONTROL; + field public static final int _SC_LINE_MAX; + field public static final int _SC_LOGIN_NAME_MAX; + field public static final int _SC_MAPPED_FILES; + field public static final int _SC_MEMLOCK; + field public static final int _SC_MEMLOCK_RANGE; + field public static final int _SC_MEMORY_PROTECTION; + field public static final int _SC_MESSAGE_PASSING; + field public static final int _SC_MQ_OPEN_MAX; + field public static final int _SC_MQ_PRIO_MAX; + field public static final int _SC_NGROUPS_MAX; + field public static final int _SC_NPROCESSORS_CONF; + field public static final int _SC_NPROCESSORS_ONLN; + field public static final int _SC_OPEN_MAX; + field public static final int _SC_PAGESIZE; + field public static final int _SC_PAGE_SIZE; + field public static final int _SC_PASS_MAX; + field public static final int _SC_PHYS_PAGES; + field public static final int _SC_PRIORITIZED_IO; + field public static final int _SC_PRIORITY_SCHEDULING; + field public static final int _SC_REALTIME_SIGNALS; + field public static final int _SC_RE_DUP_MAX; + field public static final int _SC_RTSIG_MAX; + field public static final int _SC_SAVED_IDS; + field public static final int _SC_SEMAPHORES; + field public static final int _SC_SEM_NSEMS_MAX; + field public static final int _SC_SEM_VALUE_MAX; + field public static final int _SC_SHARED_MEMORY_OBJECTS; + field public static final int _SC_SIGQUEUE_MAX; + field public static final int _SC_STREAM_MAX; + field public static final int _SC_SYNCHRONIZED_IO; + field public static final int _SC_THREADS; + field public static final int _SC_THREAD_ATTR_STACKADDR; + field public static final int _SC_THREAD_ATTR_STACKSIZE; + field public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS; + field public static final int _SC_THREAD_KEYS_MAX; + field public static final int _SC_THREAD_PRIORITY_SCHEDULING; + field public static final int _SC_THREAD_PRIO_INHERIT; + field public static final int _SC_THREAD_PRIO_PROTECT; + field public static final int _SC_THREAD_SAFE_FUNCTIONS; + field public static final int _SC_THREAD_STACK_MIN; + field public static final int _SC_THREAD_THREADS_MAX; + field public static final int _SC_TIMERS; + field public static final int _SC_TIMER_MAX; + field public static final int _SC_TTY_NAME_MAX; + field public static final int _SC_TZNAME_MAX; + field public static final int _SC_VERSION; + field public static final int _SC_XBS5_ILP32_OFF32; + field public static final int _SC_XBS5_ILP32_OFFBIG; + field public static final int _SC_XBS5_LP64_OFF64; + field public static final int _SC_XBS5_LPBIG_OFFBIG; + field public static final int _SC_XOPEN_CRYPT; + field public static final int _SC_XOPEN_ENH_I18N; + field public static final int _SC_XOPEN_LEGACY; + field public static final int _SC_XOPEN_REALTIME; + field public static final int _SC_XOPEN_REALTIME_THREADS; + field public static final int _SC_XOPEN_SHM; + field public static final int _SC_XOPEN_UNIX; + field public static final int _SC_XOPEN_VERSION; + field public static final int _SC_XOPEN_XCU_VERSION; + } + + public final class StructPollfd { + ctor public StructPollfd(); + field public short events; + field public java.io.FileDescriptor fd; + field public short revents; + field public java.lang.Object userData; + } + + public final class StructStat { + ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + field public final long st_atime; + field public final long st_blksize; + field public final long st_blocks; + field public final long st_ctime; + field public final long st_dev; + field public final int st_gid; + field public final long st_ino; + field public final int st_mode; + field public final long st_mtime; + field public final long st_nlink; + field public final long st_rdev; + field public final long st_size; + field public final int st_uid; + } + + public final class StructStatVfs { + ctor public StructStatVfs(long, long, long, long, long, long, long, long, long, long, long); + field public final long f_bavail; + field public final long f_bfree; + field public final long f_blocks; + field public final long f_bsize; + field public final long f_favail; + field public final long f_ffree; + field public final long f_files; + field public final long f_flag; + field public final long f_frsize; + field public final long f_fsid; + field public final long f_namemax; + } + + public final class StructUtsname { + ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); + field public final java.lang.String machine; + field public final java.lang.String nodename; + field public final java.lang.String release; + field public final java.lang.String sysname; + field public final java.lang.String version; + } + +} + package android.telephony { public final class CellIdentityCdma implements android.os.Parcelable { @@ -28875,6 +29583,46 @@ package android.util { method public void previousMonth(); } + public final class MutableBoolean { + ctor public MutableBoolean(boolean); + field public boolean value; + } + + public final class MutableByte { + ctor public MutableByte(byte); + field public byte value; + } + + public final class MutableChar { + ctor public MutableChar(char); + field public char value; + } + + public final class MutableDouble { + ctor public MutableDouble(double); + field public double value; + } + + public final class MutableFloat { + ctor public MutableFloat(float); + field public float value; + } + + public final class MutableInt { + ctor public MutableInt(int); + field public int value; + } + + public final class MutableLong { + ctor public MutableLong(long); + field public long value; + } + + public final class MutableShort { + ctor public MutableShort(short); + field public short value; + } + public class NoSuchPropertyException extends java.lang.RuntimeException { ctor public NoSuchPropertyException(java.lang.String); } @@ -32548,6 +33296,34 @@ package android.view.inputmethod { field public static final android.os.Parcelable.Creator CREATOR; } + public final class CursorAnchorInfo implements android.os.Parcelable { + ctor public CursorAnchorInfo(android.os.Parcel); + method public int describeContents(); + method public int getCandidatesEnd(); + method public int getCandidatesStart(); + method public android.graphics.RectF getCharacterRect(int); + method public float getInsertionMarkerBaseline(); + method public float getInsertionMarkerBottom(); + method public float getInsertionMarkerHorizontal(); + method public float getInsertionMarkerTop(); + method public android.graphics.Matrix getMatrix(); + method public int getSelectionEnd(); + method public int getSelectionStart(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class CursorAnchorInfo.CursorAnchorInfoBuilder { + ctor public CursorAnchorInfo.CursorAnchorInfoBuilder(); + method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder addCharacterRect(int, float, float, float, float); + method public android.view.inputmethod.CursorAnchorInfo build(); + method public void reset(); + method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setCandidateRange(int, int); + method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setInsertionMarkerLocation(float, float, float, float); + method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setMatrix(android.graphics.Matrix); + method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setSelectionRange(int, int); + } + public class EditorInfo implements android.text.InputType android.os.Parcelable { ctor public EditorInfo(); method public int describeContents(); @@ -32756,6 +33532,7 @@ package android.view.inputmethod { method public void toggleSoftInput(int, int); method public void toggleSoftInputFromWindow(android.os.IBinder, int, int); method public void updateCursor(android.view.View, int, int, int, int); + method public void updateCursorAnchorInfo(android.view.View, android.view.inputmethod.CursorAnchorInfo); method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText); method public void updateSelection(android.view.View, int, int, int, int); method public void viewClicked(android.view.View); @@ -32778,6 +33555,7 @@ package android.view.inputmethod { method public abstract void finishInput(); method public abstract void toggleSoftInput(int, int); method public abstract void updateCursor(android.graphics.Rect); + method public abstract void updateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo); method public abstract void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public abstract void updateSelection(int, int, int, int, int, int); method public abstract void viewClicked(boolean); @@ -33027,7 +33805,6 @@ package android.webkit { method public abstract long getResources(); method public abstract void grant(long); field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L - field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L } @@ -33618,9 +34395,11 @@ package android.widget { ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int); ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int); method public int getKeyProgressIncrement(); + method public boolean getSplitTrack(); method public android.graphics.drawable.Drawable getThumb(); method public int getThumbOffset(); method public void setKeyProgressIncrement(int); + method public void setSplitTrack(boolean); method public void setThumb(android.graphics.drawable.Drawable); method public void setThumbOffset(int); } @@ -34157,9 +34936,11 @@ package android.widget { ctor public EdgeEffect(android.content.Context); method public boolean draw(android.graphics.Canvas); method public void finish(); + method public int getMaxHeight(); method public boolean isFinished(); method public void onAbsorb(int); method public void onPull(float); + method public void onPull(float, float); method public void onRelease(); method public void setSize(int, int); } @@ -35423,6 +36204,7 @@ package android.widget { ctor public Switch(android.content.Context, android.util.AttributeSet); ctor public Switch(android.content.Context, android.util.AttributeSet, int); ctor public Switch(android.content.Context, android.util.AttributeSet, int, int); + method public boolean getSplitTrack(); method public int getSwitchMinWidth(); method public int getSwitchPadding(); method public java.lang.CharSequence getTextOff(); @@ -35431,6 +36213,7 @@ package android.widget { method public int getThumbTextPadding(); method public android.graphics.drawable.Drawable getTrackDrawable(); method public void onMeasure(int, int); + method public void setSplitTrack(boolean); method public void setSwitchMinWidth(int); method public void setSwitchPadding(int); method public void setSwitchTextAppearance(android.content.Context, int); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 36f9f4b..b4b3c99 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.NonNull; +import android.os.PersistableBundle; import android.transition.Scene; import android.transition.TransitionManager; import android.util.ArrayMap; @@ -922,6 +923,30 @@ public class Activity extends ContextThemeWrapper } /** + * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with + * the attribute {@link android.R.attr#persistable} set true. + * + * @param savedInstanceState if the activity is being re-initialized after + * previously being shut down then this Bundle contains the data it most + * recently supplied in {@link #onSaveInstanceState}. + * <b><i>Note: Otherwise it is null.</i></b> + * @param persistentState if the activity is being re-initialized after + * previously being shut down or powered off then this Bundle contains the data it most + * recently supplied to outPersistentState in {@link #onSaveInstanceState}. + * <b><i>Note: Otherwise it is null.</i></b> + * + * @see #onCreate(android.os.Bundle) + * @see #onStart + * @see #onSaveInstanceState + * @see #onRestoreInstanceState + * @see #onPostCreate + */ + protected void onCreate(@Nullable Bundle savedInstanceState, + @Nullable PersistableBundle persistentState) { + onCreate(savedInstanceState); + } + + /** * The hook for {@link ActivityThread} to restore the state of this activity. * * Calls {@link #onSaveInstanceState(android.os.Bundle)} and @@ -935,6 +960,23 @@ public class Activity extends ContextThemeWrapper } /** + * The hook for {@link ActivityThread} to restore the state of this activity. + * + * Calls {@link #onSaveInstanceState(android.os.Bundle)} and + * {@link #restoreManagedDialogs(android.os.Bundle)}. + * + * @param savedInstanceState contains the saved state + * @param persistentState contains the persistable saved state + */ + final void performRestoreInstanceState(Bundle savedInstanceState, + PersistableBundle persistentState) { + onRestoreInstanceState(savedInstanceState, persistentState); + if (savedInstanceState != null) { + restoreManagedDialogs(savedInstanceState); + } + } + + /** * This method is called after {@link #onStart} when the activity is * being re-initialized from a previously saved state, given here in * <var>savedInstanceState</var>. Most implementations will simply use {@link #onCreate} @@ -962,7 +1004,34 @@ public class Activity extends ContextThemeWrapper } } } - + + /** + * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. The {@link + * android.os.PersistableBundle} passed came from the restored PersistableBundle first + * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. + * + * <p>This method is called between {@link #onStart} and + * {@link #onPostCreate}. + * + * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called. + * + * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. + * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}. + * + * @see #onRestoreInstanceState(Bundle) + * @see #onCreate + * @see #onPostCreate + * @see #onResume + * @see #onSaveInstanceState + */ + protected void onRestoreInstanceState(Bundle savedInstanceState, + PersistableBundle persistentState) { + if (savedInstanceState != null) { + onRestoreInstanceState(savedInstanceState); + } + } + /** * Restore the state of any saved managed dialogs. * @@ -1039,6 +1108,21 @@ public class Activity extends ContextThemeWrapper } /** + * This is the same as {@link #onPostCreate(Bundle)} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. + * + * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState} + * @param persistentState The data caming from the PersistableBundle first + * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. + * + * @see #onCreate + */ + protected void onPostCreate(@Nullable Bundle savedInstanceState, + @Nullable PersistableBundle persistentState) { + onPostCreate(savedInstanceState); + } + + /** * Called after {@link #onCreate} — or after {@link #onRestart} when * the activity had been stopped, but is now again being displayed to the * user. It will be followed by {@link #onResume}. @@ -1194,6 +1278,22 @@ public class Activity extends ContextThemeWrapper } /** + * The hook for {@link ActivityThread} to save the state of this activity. + * + * Calls {@link #onSaveInstanceState(android.os.Bundle)} + * and {@link #saveManagedDialogs(android.os.Bundle)}. + * + * @param outState The bundle to save the state to. + * @param outPersistentState The bundle to save persistent state to. + */ + final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + onSaveInstanceState(outState, outPersistentState); + saveManagedDialogs(outState); + if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + + ", " + outPersistentState); + } + + /** * Called to retrieve per-instance state from an activity before being killed * so that the state can be restored in {@link #onCreate} or * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method @@ -1248,6 +1348,25 @@ public class Activity extends ContextThemeWrapper } /** + * This is the same as {@link #onSaveInstanceState} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. The {@link + * android.os.PersistableBundle} passed in will be saved and presented in + * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity + * is restarted following the next device reboot. + * + * @param outState Bundle in which to place your saved state. + * @param outPersistentState State which will be saved across reboots. + * + * @see #onSaveInstanceState(Bundle) + * @see #onCreate + * @see #onRestoreInstanceState(Bundle, PersistableBundle) + * @see #onPause + */ + protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + onSaveInstanceState(outState); + } + + /** * Save the state of any managed dialogs. * * @param outState place to store the saved state. @@ -3499,13 +3618,15 @@ public class Activity extends ContextThemeWrapper } // Get the primary color and update the RecentsActivityValues for this activity - TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme); - int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0); - a.recycle(); - if (colorPrimary != 0) { - ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues(); - v.colorPrimary = colorPrimary; - setRecentsActivityValues(v); + if (theme != null) { + TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme); + int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0); + a.recycle(); + if (colorPrimary != 0) { + ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues(); + v.colorPrimary = colorPrimary; + setRecentsActivityValues(v); + } } } @@ -4807,6 +4928,7 @@ public class Activity extends ContextThemeWrapper public void setRecentsActivityValues(ActivityManager.RecentsActivityValues values) { ActivityManager.RecentsActivityValues activityValues = new ActivityManager.RecentsActivityValues(values); + // Scale the icon down to something reasonable if (values.icon != null) { final int size = ActivityManager.getLauncherLargeIconSizeInner(this); activityValues.icon = Bitmap.createScaledBitmap(values.icon, size, size, true); @@ -5488,13 +5610,22 @@ public class Activity extends ContextThemeWrapper return mParent != null ? mParent.getActivityToken() : mToken; } - final void performCreate(Bundle icicle) { - onCreate(icicle); + final void performCreateCommon() { mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); } + final void performCreate(Bundle icicle) { + onCreate(icicle); + performCreateCommon(); + } + + final void performCreate(Bundle icicle, PersistableBundle persistentState) { + onCreate(icicle, persistentState); + performCreateCommon(); + } + final void performStart() { mFragments.noteStateNotSaved(); mCalled = false; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d809d8..044727d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -20,7 +20,6 @@ import android.os.BatteryStats; import android.os.IBinder; import com.android.internal.app.IUsageStats; import com.android.internal.app.ProcessStats; -import com.android.internal.os.PkgUsageStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -2130,14 +2129,15 @@ public class ActivityManager { return new HashMap<String, Integer>(); } - PkgUsageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats(); + UsageStats.PackageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats( + ActivityThread.currentPackageName()); if (allPkgUsageStats == null) { return new HashMap<String, Integer>(); } Map<String, Integer> launchCounts = new HashMap<String, Integer>(); - for (PkgUsageStats pkgUsageStats : allPkgUsageStats) { - launchCounts.put(pkgUsageStats.packageName, pkgUsageStats.launchCount); + for (UsageStats.PackageStats pkgUsageStats : allPkgUsageStats) { + launchCounts.put(pkgUsageStats.getPackageName(), pkgUsageStats.getLaunchCount()); } return launchCounts; @@ -2251,17 +2251,17 @@ public class ActivityManager { * * @hide */ - public PkgUsageStats[] getAllPackageUsageStats() { + public UsageStats.PackageStats[] getAllPackageUsageStats() { try { IUsageStats usageStatsService = IUsageStats.Stub.asInterface( ServiceManager.getService("usagestats")); if (usageStatsService != null) { - return usageStatsService.getAllPkgUsageStats(); + return usageStatsService.getAllPkgUsageStats(ActivityThread.currentPackageName()); } } catch (RemoteException e) { Log.w(TAG, "Could not query usage stats", e); } - return new PkgUsageStats[0]; + return new UsageStats.PackageStats[0]; } /** diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 57da21e..ec2868a 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -40,6 +40,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -454,7 +455,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case ACTIVITY_PAUSED_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - activityPaused(token); + PersistableBundle persistentState = data.readPersistableBundle(); + activityPaused(token, persistentState); reply.writeNoException(); return true; } @@ -463,10 +465,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); Bundle map = data.readBundle(); - Bitmap thumbnail = data.readInt() != 0 - ? Bitmap.CREATOR.createFromParcel(data) : null; + PersistableBundle persistentState = data.readPersistableBundle(); CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); - activityStopped(token, map, thumbnail, description); + activityStopped(token, map, persistentState, description); reply.writeNoException(); return true; } @@ -2583,31 +2584,27 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - public void activityPaused(IBinder token) throws RemoteException + public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); + data.writePersistableBundle(persistentState); mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } public void activityStopped(IBinder token, Bundle state, - Bitmap thumbnail, CharSequence description) throws RemoteException + PersistableBundle persistentState, CharSequence description) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); data.writeBundle(state); - if (thumbnail != null) { - data.writeInt(1); - thumbnail.writeToParcel(data, 0); - } else { - data.writeInt(0); - } + data.writePersistableBundle(persistentState); TextUtils.writeToParcel(description, data, 0); mRemote.transact(ACTIVITY_STOPPED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); reply.readException(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b606088..161cb76 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -56,11 +56,11 @@ import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; import android.os.IBinder; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -69,8 +69,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; -import android.transition.Scene; -import android.transition.TransitionManager; import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.ArrayMap; @@ -268,6 +266,7 @@ public final class ActivityThread { Intent intent; IVoiceInteractor voiceInteractor; Bundle state; + PersistableBundle persistentState; Activity activity; Window window; Activity parent; @@ -317,6 +316,10 @@ public final class ActivityThread { return false; } + public boolean isPersistable() { + return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + } + public String toString() { ComponentName componentName = intent != null ? intent.getComponent() : null; return "ActivityRecord{" @@ -605,8 +608,8 @@ public final class ActivityThread { // activity itself back to the activity manager. (matters more with ipc) public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, - int procState, Bundle state, List<ResultInfo> pendingResults, + IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) { @@ -622,6 +625,7 @@ public final class ActivityThread { r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; + r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; @@ -2205,7 +2209,11 @@ public final class ActivityThread { } activity.mCalled = false; - mInstrumentation.callActivityOnCreate(activity, r.state); + if (r.isPersistable()) { + mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); + } else { + mInstrumentation.callActivityOnCreate(activity, r.state); + } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + @@ -2218,13 +2226,23 @@ public final class ActivityThread { r.stopped = false; } if (!r.activity.mFinished) { - if (r.state != null) { + if (r.isPersistable()) { + if (r.state != null || r.persistentState != null) { + mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, + r.persistentState); + } + } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; - mInstrumentation.callActivityOnPostCreate(activity, r.state); + if (r.isPersistable()) { + mInstrumentation.callActivityOnPostCreate(activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnPostCreate(activity, r.state); + } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + @@ -2842,6 +2860,7 @@ public final class ActivityThread { r.paused = false; r.stopped = false; r.state = null; + r.persistentState = null; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( @@ -3069,7 +3088,7 @@ public final class ActivityThread { // Tell the activity manager we have paused. try { - ActivityManagerNative.getDefault().activityPaused(token); + ActivityManagerNative.getDefault().activityPaused(token, r.persistentState); } catch (RemoteException ex) { } } @@ -3099,17 +3118,13 @@ public final class ActivityThread { + r.intent.getComponent().toShortString()); Slog.e(TAG, e.getMessage(), e); } - Bundle state = null; if (finished) { r.activity.mFinished = true; } try { // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { - state = new Bundle(); - state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); - r.state = state; + callCallActivityOnSaveInstanceState(r); } // Now we are idle. r.activity.mCalled = false; @@ -3145,7 +3160,7 @@ public final class ActivityThread { listeners.get(i).onPaused(r.activity); } - return state; + return !r.activity.mFinished && saveState ? r.state : null; } final void performStopActivity(IBinder token, boolean saveState) { @@ -3156,7 +3171,7 @@ public final class ActivityThread { private static class StopInfo implements Runnable { ActivityClientRecord activity; Bundle state; - Bitmap thumbnail; + PersistableBundle persistentState; CharSequence description; @Override public void run() { @@ -3164,7 +3179,7 @@ public final class ActivityThread { try { if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity); ActivityManagerNative.getDefault().activityStopped( - activity.token, state, thumbnail, description); + activity.token, state, persistentState, description); } catch (RemoteException ex) { } } @@ -3203,7 +3218,6 @@ public final class ActivityThread { private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown, boolean saveState) { if (localLOGV) Slog.v(TAG, "Performing stop of " + r); - Bundle state = null; if (r != null) { if (!keepShown && r.stopped) { if (r.activity.mFinished) { @@ -3223,7 +3237,6 @@ public final class ActivityThread { // First create a thumbnail for the activity... // For now, don't create the thumbnail here; we are // doing that by doing a screen snapshot. - info.thumbnail = null; //createThumbnailBitmap(r); info.description = r.activity.onCreateDescription(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { @@ -3238,12 +3251,7 @@ public final class ActivityThread { // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { if (r.state == null) { - state = new Bundle(); - state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); - r.state = state; - } else { - state = r.state; + callCallActivityOnSaveInstanceState(r); } } @@ -3319,6 +3327,7 @@ public final class ActivityThread { // manager to proceed and allow us to go fully into the background. info.activity = r; info.state = r.state; + info.persistentState = r.persistentState; mH.post(info); } @@ -3775,9 +3784,7 @@ public final class ActivityThread { performPauseActivity(r.token, false, r.isPreHoneycomb()); } if (r.state == null && !r.stopped && !r.isPreHoneycomb()) { - r.state = new Bundle(); - r.state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); + callCallActivityOnSaveInstanceState(r); } handleDestroyActivity(r.token, false, configChanges, true); @@ -3807,6 +3814,18 @@ public final class ActivityThread { handleLaunchActivity(r, currentIntent); } + private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) { + r.state = new Bundle(); + r.state.setAllowFds(false); + if (r.isPersistable()) { + r.persistentState = new PersistableBundle(); + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); + } + } + ArrayList<ComponentCallbacks2> collectComponentCallbacks( boolean allActivities, Configuration newConfig) { ArrayList<ComponentCallbacks2> callbacks diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b616c1e..d813dab 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; @@ -184,8 +185,10 @@ public class AppOpsManager { public static final int OP_MONITOR_LOCATION = 41; /** @hide Continually monitoring location data with a relatively high power request. */ public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42; + /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */ + public static final int OP_GET_USAGE_STATS = 43; /** @hide */ - public static final int _NUM_OP = 43; + public static final int _NUM_OP = 44; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -252,6 +255,7 @@ public class AppOpsManager { OP_WAKE_LOCK, OP_COARSE_LOCATION, OP_COARSE_LOCATION, + OP_GET_USAGE_STATS, }; /** @@ -302,6 +306,7 @@ public class AppOpsManager { null, OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, + null, }; /** @@ -352,6 +357,7 @@ public class AppOpsManager { "WAKE_LOCK", "MONITOR_LOCATION", "MONITOR_HIGH_POWER_LOCATION", + "GET_USAGE_STATS" }; /** @@ -402,6 +408,7 @@ public class AppOpsManager { android.Manifest.permission.WAKE_LOCK, null, // no permission for generic location monitoring null, // no permission for high power location monitoring + android.Manifest.permission.PACKAGE_USAGE_STATS, }; /** @@ -451,6 +458,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS }; /** @@ -504,6 +512,7 @@ public class AppOpsManager { false, false, false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 7f2fb59..e7902a9 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; @@ -141,6 +142,7 @@ public abstract class ApplicationThreadNative extends Binder data.readStrongBinder()); int procState = data.readInt(); Bundle state = data.readBundle(); + PersistableBundle persistentState = data.readPersistableBundle(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); boolean notResumed = data.readInt() != 0; @@ -151,9 +153,9 @@ public abstract class ApplicationThreadNative extends Binder boolean autoStopProfiler = data.readInt() != 0; Bundle resumeArgs = data.readBundle(); scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, - voiceInteractor, procState, state, - ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler, - resumeArgs); + voiceInteractor, procState, state, persistentState, + ri, pi, notResumed, isForward, profileName, profileFd, + autoStopProfiler, resumeArgs); return true; } @@ -731,8 +733,8 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, - int procState, Bundle state, List<ResultInfo> pendingResults, + IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) @@ -748,6 +750,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null); data.writeInt(procState); data.writeBundle(state); + data.writePersistableBundle(persistentState); data.writeTypedList(pendingResults); data.writeTypedList(pendingNewIntents); data.writeInt(notResumed ? 1 : 0); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5ed5030..801182d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -678,6 +678,11 @@ class ContextImpl extends Context { return new NetworkScoreManager(ctx); } }); + + registerService(USAGE_STATS_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new UsageStatsManager(ctx.getOuterContext()); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 2e9cdf3b7..074b427 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -45,6 +45,7 @@ import android.os.IInterface; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.StrictMode; import android.service.voice.IVoiceInteractionSession; @@ -104,9 +105,9 @@ public interface IActivityManager extends IInterface { public void activityResumed(IBinder token) throws RemoteException; public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) throws RemoteException; - public void activityPaused(IBinder token) throws RemoteException; + public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException; public void activityStopped(IBinder token, Bundle state, - Bitmap thumbnail, CharSequence description) throws RemoteException; + PersistableBundle persistentState, CharSequence description) throws RemoteException; public void activitySlept(IBinder token) throws RemoteException; public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index fefba8a..a832034 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.Debug; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; @@ -58,8 +59,8 @@ public interface IApplicationThread extends IInterface { void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, IVoiceInteractor voiceInteractor, int procState, Bundle state, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, - boolean isForward, + PersistableBundle persistentState, List<ResultInfo> pendingResults, + List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) throws RemoteException; diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 347de97..8ab9ac3 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -43,4 +43,5 @@ interface IUiAutomationConnection { WindowContentFrameStats getWindowContentFrameStats(int windowId); void clearWindowAnimationFrameStats(); WindowAnimationFrameStats getWindowAnimationFrameStats(); + void executeShellCommand(String command, in ParcelFileDescriptor fd); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index e58ccb8..bb3e002 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -30,6 +30,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.MessageQueue; import android.os.PerformanceCollector; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -1061,15 +1062,7 @@ public class Instrumentation { return (Activity)cl.loadClass(className).newInstance(); } - /** - * Perform calling of an activity's {@link Activity#onCreate} - * method. The default implementation simply calls through to that method. - * - * @param activity The activity being created. - * @param icicle The previously frozen state (or null) to pass through to - * onCreate(). - */ - public void callActivityOnCreate(Activity activity, Bundle icicle) { + private void prePerformCreate(Activity activity) { if (mWaitingActivities != null) { synchronized (mSync) { final int N = mWaitingActivities.size(); @@ -1083,9 +1076,9 @@ public class Instrumentation { } } } - - activity.performCreate(icicle); - + } + + private void postPerformCreate(Activity activity) { if (mActivityMonitors != null) { synchronized (mSync) { final int N = mActivityMonitors.size(); @@ -1096,6 +1089,33 @@ public class Instrumentation { } } } + + /** + * Perform calling of an activity's {@link Activity#onCreate} + * method. The default implementation simply calls through to that method. + * + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to onCreate(). + */ + public void callActivityOnCreate(Activity activity, Bundle icicle) { + prePerformCreate(activity); + activity.performCreate(icicle); + postPerformCreate(activity); + } + + /** + * Perform calling of an activity's {@link Activity#onCreate} + * method. The default implementation simply calls through to that method. + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to + * @param persistentState The previously persisted state (or null) + */ + public void callActivityOnCreate(Activity activity, Bundle icicle, + PersistableBundle persistentState) { + prePerformCreate(activity); + activity.performCreate(icicle, persistentState); + postPerformCreate(activity); + } public void callActivityOnDestroy(Activity activity) { // TODO: the following block causes intermittent hangs when using startActivity @@ -1130,7 +1150,7 @@ public class Instrumentation { /** * Perform calling of an activity's {@link Activity#onRestoreInstanceState} * method. The default implementation simply calls through to that method. - * + * * @param activity The activity being restored. * @param savedInstanceState The previously saved state being restored. */ @@ -1139,9 +1159,22 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onRestoreInstanceState} + * method. The default implementation simply calls through to that method. + * + * @param activity The activity being restored. + * @param savedInstanceState The previously saved state being restored. + * @param persistentState The previously persisted state (or null) + */ + public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState, + PersistableBundle persistentState) { + activity.performRestoreInstanceState(savedInstanceState, persistentState); + } + + /** * Perform calling of an activity's {@link Activity#onPostCreate} method. * The default implementation simply calls through to that method. - * + * * @param activity The activity being created. * @param icicle The previously frozen state (or null) to pass through to * onPostCreate(). @@ -1151,6 +1184,19 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onPostCreate} method. + * The default implementation simply calls through to that method. + * + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to + * onPostCreate(). + */ + public void callActivityOnPostCreate(Activity activity, Bundle icicle, + PersistableBundle persistentState) { + activity.onPostCreate(icicle, persistentState); + } + + /** * Perform calling of an activity's {@link Activity#onNewIntent} * method. The default implementation simply calls through to that method. * @@ -1215,7 +1261,7 @@ public class Instrumentation { /** * Perform calling of an activity's {@link Activity#onSaveInstanceState} * method. The default implementation simply calls through to that method. - * + * * @param activity The activity being saved. * @param outState The bundle to pass to the call. */ @@ -1224,6 +1270,18 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onSaveInstanceState} + * method. The default implementation simply calls through to that method. + * @param activity The activity being saved. + * @param outState The bundle to pass to the call. + * @param outPersistentState The persistent bundle to pass to the call. + */ + public void callActivityOnSaveInstanceState(Activity activity, Bundle outState, + PersistableBundle outPersistentState) { + activity.performSaveInstanceState(outState, outPersistentState); + } + + /** * Perform calling of an activity's {@link Activity#onPause} method. The * default implementation simply calls through to that method. * @@ -1428,7 +1486,7 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, * but accepts an array of activities to be started. Note that active * {@link ActivityMonitor} objects only match against the first activity in * the array. @@ -1442,7 +1500,7 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, * but accepts an array of activities to be started. Note that active * {@link ActivityMonitor} objects only match against the first activity in * the array. @@ -1545,7 +1603,8 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, but for starting as a particular user. + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, + * but for starting as a particular user. * * @param who The Context from which the activity is being started. * @param contextThread The main thread of the Context from which the activity diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index aab6ed8..db91742a 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -44,8 +44,8 @@ public class KeyguardManager { * you to disable / reenable the keyguard. */ public class KeyguardLock { - private IBinder mToken = new Binder(); - private String mTag; + private final IBinder mToken = new Binder(); + private final String mTag; KeyguardLock(String tag) { mTag = tag; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index bba6caf..76a6a8e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -659,8 +659,8 @@ public class Notification implements Parcelable /** * @hide - * Extra added by NotificationManagerService to indicate whether a NotificationScorer - * modified the Notifications's score. + * Extra added by NotificationManagerService to indicate whether + * the Notifications's score has been modified. */ public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 9405325..64e3484 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -26,6 +26,7 @@ import android.graphics.Canvas; import android.graphics.Point; import android.hardware.display.DisplayManagerGlobal; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; @@ -40,7 +41,9 @@ import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; +import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -840,6 +843,44 @@ public final class UiAutomation { return null; } + /** + * Executes a shell command. This method returs a file descriptor that points + * to the standard output stream. The command execution is similar to running + * "adb shell <command>" from a host connected to the device. + * <p> + * <strong>Note:</strong> It is your responsibility to close the retunred file + * descriptor once you are done reading. + * </p> + * + * @param command The command to execute. + * @return A file descriptor to the standard output stream. + */ + public ParcelFileDescriptor executeShellCommand(String command) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + + ParcelFileDescriptor source = null; + ParcelFileDescriptor sink = null; + + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + source = pipe[0]; + sink = pipe[1]; + + // Calling out without a lock held. + mUiAutomationConnection.executeShellCommand(command, sink); + } catch (IOException ioe) { + Log.e(LOG_TAG, "Error executing shell command!", ioe); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error executing shell command!", re); + } finally { + IoUtils.closeQuietly(sink); + } + + return source; + } + private static float getDegreesForRotation(int value) { switch (value) { case Surface.ROTATION_90: { diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index fa40286..81bcb39 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -23,6 +23,7 @@ import android.graphics.Bitmap; import android.hardware.input.InputManager; import android.os.Binder; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -33,6 +34,12 @@ import android.view.WindowAnimationFrameStats; import android.view.WindowContentFrameStats; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.IAccessibilityManager; +import libcore.io.IoUtils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** * This is a remote object that is passed from the shell to an instrumentation @@ -50,8 +57,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Service.WINDOW_SERVICE)); - private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface( - ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); + private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub + .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); private final Object mLock = new Object(); @@ -220,6 +227,41 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override + public void executeShellCommand(String command, ParcelFileDescriptor sink) + throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + + InputStream in = null; + OutputStream out = null; + + try { + java.lang.Process process = Runtime.getRuntime().exec(command); + + in = process.getInputStream(); + out = new FileOutputStream(sink.getFileDescriptor()); + + final byte[] buffer = new byte[8192]; + while (true) { + final int readByteCount = in.read(buffer); + if (readByteCount < 0) { + break; + } + out.write(buffer, 0, readByteCount); + } + } catch (IOException ioe) { + throw new RuntimeException("Error running shell command", ioe); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + IoUtils.closeQuietly(sink); + } + } + + @Override public void shutdown() { synchronized (mLock) { if (isConnectedLocked()) { diff --git a/core/java/android/app/UsageStats.aidl b/core/java/android/app/UsageStats.aidl new file mode 100644 index 0000000..7dee70a --- /dev/null +++ b/core/java/android/app/UsageStats.aidl @@ -0,0 +1,20 @@ +/** + * 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.app; + +parcelable UsageStats; +parcelable UsageStats.PackageStats; diff --git a/core/java/android/app/UsageStats.java b/core/java/android/app/UsageStats.java new file mode 100644 index 0000000..0aeba59 --- /dev/null +++ b/core/java/android/app/UsageStats.java @@ -0,0 +1,406 @@ +/* + * 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.app; + +import android.content.res.Configuration; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * Snapshot of current usage stats data. + * @hide + */ +public class UsageStats implements Parcelable { + /** @hide */ + public final ArrayMap<String, PackageStats> mPackages = new ArrayMap<String, PackageStats>(); + /** @hide */ + public final ArrayMap<Configuration, ConfigurationStats> mConfigurations + = new ArrayMap<Configuration, ConfigurationStats>(); + + public static class PackageStats implements Parcelable { + private final String mPackageName; + private int mLaunchCount; + private long mUsageTime; + private long mResumedTime; + + /** @hide */ + public final ArrayMap<String, Long> componentResumeTimes; + + public static final Parcelable.Creator<PackageStats> CREATOR + = new Parcelable.Creator<PackageStats>() { + public PackageStats createFromParcel(Parcel in) { + return new PackageStats(in); + } + + public PackageStats[] newArray(int size) { + return new PackageStats[size]; + } + }; + + public String toString() { + return "PackageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mPackageName + "}"; + } + + /** @hide */ + public PackageStats(String pkgName) { + mPackageName = pkgName; + componentResumeTimes = new ArrayMap<String, Long>(); + } + + /** @hide */ + public PackageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { + mPackageName = pkgName; + mLaunchCount = count; + mUsageTime = time; + componentResumeTimes = new ArrayMap<String, Long>(); + componentResumeTimes.putAll(lastResumeTimes); + } + + /** @hide */ + public PackageStats(Parcel source) { + mPackageName = source.readString(); + mLaunchCount = source.readInt(); + mUsageTime = source.readLong(); + final int N = source.readInt(); + componentResumeTimes = new ArrayMap<String, Long>(N); + for (int i = 0; i < N; i++) { + String component = source.readString(); + long lastResumeTime = source.readLong(); + componentResumeTimes.put(component, lastResumeTime); + } + } + + /** @hide */ + public PackageStats(PackageStats pStats) { + mPackageName = pStats.mPackageName; + mLaunchCount = pStats.mLaunchCount; + mUsageTime = pStats.mUsageTime; + componentResumeTimes = new ArrayMap<String, Long>(pStats.componentResumeTimes); + } + + /** @hide */ + public void resume(boolean launched) { + if (launched) { + mLaunchCount++; + } + mResumedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void pause() { + if (mResumedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mResumedTime; + } + mResumedTime = 0; + } + + public final String getPackageName() { + return mPackageName; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mResumedTime > 0 ? (elapsedRealtime- mResumedTime) : 0); + } + + public final int getLaunchCount() { + return mLaunchCount; + } + + /** @hide */ + public boolean clearUsageTimes() { + mLaunchCount = 0; + mUsageTime = 0; + return mResumedTime <= 0 && componentResumeTimes.isEmpty(); + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + dest.writeString(mPackageName); + dest.writeInt(mLaunchCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + dest.writeInt(componentResumeTimes.size()); + for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { + dest.writeString(ent.getKey()); + dest.writeLong(ent.getValue()); + } + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + public static class ConfigurationStats implements Parcelable { + private final Configuration mConfiguration; + private long mLastUsedTime; + private int mUsageCount; + private long mUsageTime; + private long mStartedTime; + + public static final Parcelable.Creator<ConfigurationStats> CREATOR + = new Parcelable.Creator<ConfigurationStats>() { + public ConfigurationStats createFromParcel(Parcel in) { + return new ConfigurationStats(in); + } + + public ConfigurationStats[] newArray(int size) { + return new ConfigurationStats[size]; + } + }; + + public String toString() { + return "ConfigurationStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mConfiguration + "}"; + } + + /** @hide */ + public ConfigurationStats(Configuration config) { + mConfiguration = config; + } + + /** @hide */ + public ConfigurationStats(Parcel source) { + mConfiguration = Configuration.CREATOR.createFromParcel(source); + mLastUsedTime = source.readLong(); + mUsageCount = source.readInt(); + mUsageTime = source.readLong(); + } + + /** @hide */ + public ConfigurationStats(ConfigurationStats pStats) { + mConfiguration = pStats.mConfiguration; + mLastUsedTime = pStats.mLastUsedTime; + mUsageCount = pStats.mUsageCount; + mUsageTime = pStats.mUsageTime; + } + + public final Configuration getConfiguration() { + return mConfiguration; + } + + public final long getLastUsedTime() { + return mLastUsedTime; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mStartedTime > 0 ? (elapsedRealtime- mStartedTime) : 0); + } + + public final int getUsageCount() { + return mUsageCount; + } + + /** @hide */ + public void start() { + mLastUsedTime = System.currentTimeMillis(); + mUsageCount++; + mStartedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void stop() { + if (mStartedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mStartedTime; + } + mStartedTime = 0; + } + + /** @hide */ + public boolean clearUsageTimes() { + mUsageCount = 0; + mUsageTime = 0; + return mLastUsedTime == 0 && mStartedTime <= 0; + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + mConfiguration.writeToParcel(dest, parcelableFlags); + dest.writeLong(mLastUsedTime); + dest.writeInt(mUsageCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + /** @hide */ + public UsageStats() { + } + + /** @hide */ + public UsageStats(Parcel source, boolean extended) { + int N = source.readInt(); + for (int i=0; i<N; i++) { + PackageStats pkg = extended ? onNewPackageStats(source) : new PackageStats(source); + mPackages.put(pkg.getPackageName(), pkg); + } + N = source.readInt(); + for (int i=0; i<N; i++) { + ConfigurationStats config = extended ? onNewConfigurationStats(source) + : new ConfigurationStats(source); + mConfigurations.put(config.getConfiguration(), config); + } + } + + public int getPackageStatsCount() { + return mPackages.size(); + } + + public PackageStats getPackageStatsAt(int index) { + return mPackages.valueAt(index); + } + + public PackageStats getPackageStats(String pkgName) { + return mPackages.get(pkgName); + } + + /** @hide */ + public PackageStats getOrCreatePackageStats(String pkgName) { + PackageStats ps = mPackages.get(pkgName); + if (ps == null) { + ps = onNewPackageStats(pkgName); + mPackages.put(pkgName, ps); + } + return ps; + } + + public int getConfigurationStatsCount() { + return mConfigurations.size(); + } + + public ConfigurationStats getConfigurationStatsAt(int index) { + return mConfigurations.valueAt(index); + } + + public ConfigurationStats getConfigurationStats(Configuration config) { + return mConfigurations.get(config); + } + + /** @hide */ + public ConfigurationStats getOrCreateConfigurationStats(Configuration config) { + ConfigurationStats cs = mConfigurations.get(config); + if (cs == null) { + cs = onNewConfigurationStats(config); + mConfigurations.put(config, cs); + } + return cs; + } + + /** @hide */ + public void clearUsageTimes() { + for (int i=mPackages.size()-1; i>=0; i--) { + if (mPackages.valueAt(i).clearUsageTimes()) { + mPackages.removeAt(i); + } + } + for (int i=mConfigurations.size()-1; i>=0; i--) { + if (mConfigurations.valueAt(i).clearUsageTimes()) { + mConfigurations.removeAt(i); + } + } + } + + /** @hide */ + public PackageStats onNewPackageStats(String pkgName) { + return new PackageStats(pkgName); + } + + /** @hide */ + public PackageStats onNewPackageStats(Parcel source) { + return new PackageStats(source); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigurationStats(config); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigurationStats(source); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, false); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, true); + } + + private void writeToParcelInner(Parcel dest, int parcelableFlags, boolean extended) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + + int N = mPackages.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + PackageStats ps = mPackages.valueAt(i); + ps.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + ps.writeExtendedToParcel(dest, parcelableFlags); + } + } + N = mConfigurations.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + ConfigurationStats cs = mConfigurations.valueAt(i); + cs.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + cs.writeExtendedToParcel(dest, parcelableFlags); + } + } + } + + public static final Parcelable.Creator<UsageStats> CREATOR + = new Parcelable.Creator<UsageStats>() { + public UsageStats createFromParcel(Parcel in) { + return new UsageStats(in, false); + } + + public UsageStats[] newArray(int size) { + return new UsageStats[size]; + } + }; +} diff --git a/core/java/android/app/UsageStatsManager.java b/core/java/android/app/UsageStatsManager.java new file mode 100644 index 0000000..fbf9c3b --- /dev/null +++ b/core/java/android/app/UsageStatsManager.java @@ -0,0 +1,51 @@ +/* + * 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.app; + +import android.content.Context; +import android.os.ParcelableParcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import com.android.internal.app.IUsageStats; + +/** + * Access to usage stats data. + * @hide + */ +public class UsageStatsManager { + final Context mContext; + final IUsageStats mService; + + /** @hide */ + public UsageStatsManager(Context context) { + mContext = context; + mService = IUsageStats.Stub.asInterface(ServiceManager.getService( + Context.USAGE_STATS_SERVICE)); + } + + public UsageStats getCurrentStats() { + try { + ParcelableParcel in = mService.getCurrentStats(mContext.getOpPackageName()); + if (in != null) { + return new UsageStats(in.getParcel(), false); + } + } catch (RemoteException e) { + // About to die. + } + return new UsageStats(); + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 61ff60a..58049fd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16,8 +16,6 @@ package android.app.admin; -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; @@ -39,6 +37,8 @@ import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; @@ -359,8 +359,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current minimum password quality for all admins - * or a particular one. + * Retrieve the current minimum password quality for all admins of this user + * and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -412,8 +412,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current minimum password length for all admins - * or a particular one. + * Retrieve the current minimum password length for all admins of this + * user and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -467,8 +467,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of upper case letters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -527,8 +528,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of lower case letters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -644,8 +646,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of numerical digits required in the password - * for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)} + * for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumNumeric(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -760,8 +763,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of non-letter characters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -868,9 +872,10 @@ public class DevicePolicyManager { /** * Get the current password expiration time for the given admin or an aggregate of - * all admins if admin is null. If the password is expired, this will return the time since - * the password expired as a negative number. If admin is null, then a composite of all - * expiration timeouts is returned - which will be the minimum of all timeouts. + * all admins of this user and its profiles if admin is null. If the password is + * expired, this will return the time since the password expired as a negative number. + * If admin is null, then a composite of all expiration timeouts is returned + * - which will be the minimum of all timeouts. * * @param admin The name of the admin component to check, or null to aggregate all admins. * @return The password expiration time, in ms. @@ -887,8 +892,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current password history length for all admins - * or a particular one. + * Retrieve the current password history length for all admins of this + * user and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. * @return The length of the password history @@ -923,14 +928,13 @@ public class DevicePolicyManager { /** * Determine whether the current password the user has set is sufficient * to meet the policy requirements (quality, minimum length) that have been - * requested. + * requested by the admins of this user and its profiles. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * - * @return Returns true if the password meets the current requirements, - * else false. + * @return Returns true if the password meets the current requirements, else false. */ public boolean isActivePasswordSufficient() { if (mService != null) { @@ -993,7 +997,7 @@ public class DevicePolicyManager { /** * Retrieve the current maximum number of login attempts that are allowed - * before the device wipes itself, for all admins + * before the device wipes itself, for all admins of this user and its profiles * or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. @@ -1037,6 +1041,8 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * + * Can not be called from a managed profile. + * * @param password The new password for the user. * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}. * @return Returns true if the password was applied, or false if it is @@ -1077,8 +1083,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current maximum time to unlock for all admins - * or a particular one. + * Retrieve the current maximum time to unlock for all admins of this user + * and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -2125,4 +2131,51 @@ public class DevicePolicyManager { return null; } + + /** + * Sets which components may enter lock task mode. + * + * This function can only be called by the device owner or the profile owner. + * @param components The list of components allowed to enter lock task mode + */ + public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + if (mService != null) { + try { + mService.setLockTaskComponents(components); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * This function returns the list of components allowed to start the lock task mode. + * @hide + */ + public ComponentName[] getLockTaskComponents() { + if (mService != null) { + try { + return mService.getLockTaskComponents(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return null; + } + + /** + * This function lets the caller know whether the given component is allowed to start the + * lock task mode. + * @param component The component to check + */ + public boolean isLockTaskPermitted(ComponentName component) { + if (mService != null) { + try { + return mService.isLockTaskPermitted(component); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 0096580..03ced0f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -129,4 +129,8 @@ interface IDevicePolicyManager { void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); String[] getAccountTypesWithManagementDisabled(); + + void setLockTaskComponents(in ComponentName[] components); + ComponentName[] getLockTaskComponents(); + boolean isLockTaskPermitted(in ComponentName component); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 042ee28..a059e48 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2683,6 +2683,16 @@ public abstract class Context { public static final String NETWORK_SCORE_SERVICE = "network_score"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.UsageStatsManager} for interacting with the status bar. + * + * @see #getSystemService + * @see android.app.UsageStatsManager + * @hide + */ + public static final String USAGE_STATS_SERVICE = "usagestats"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 52a169b..eb2c11f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -936,13 +936,21 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has at least one camera pointing in - * some direction. + * some direction, or can support an external camera being connected to it. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device can support having an external camera connected to it. + * The external camera may not always be connected or available to applications to use. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports flash. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index f161f3a..0ca9161 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -458,7 +458,19 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * brightness</p> * <p>For example, if EV step is 0.333, '6' will mean an * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1</p> + * compensation of -1 EV. Note that this control will only be effective + * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when + * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>In the event of exposure compensation value being changed, camera device + * may take several frames to reach the newly requested exposure target. + * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING + * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will + * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or + * FLASH_REQUIRED (if the scene is too dark for still capture).</p> + * + * @see CaptureRequest#CONTROL_AE_LOCK + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureResult#CONTROL_AE_STATE */ public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION = new Key<Integer>("android.control.aeExposureCompensation", int.class); @@ -469,6 +481,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / * ON_AUTO_FLASH_REDEYE.</p> + * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock + * is ON, the camera device will still adjust its exposure value.</p> * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) * when AE is already locked, the camera device will not change the exposure time * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}) @@ -477,6 +491,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p> * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p> * + * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 51ea447..42a3de8 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -275,7 +275,19 @@ public final class CaptureResult extends CameraMetadata { * brightness</p> * <p>For example, if EV step is 0.333, '6' will mean an * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1</p> + * compensation of -1 EV. Note that this control will only be effective + * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when + * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>In the event of exposure compensation value being changed, camera device + * may take several frames to reach the newly requested exposure target. + * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING + * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will + * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or + * FLASH_REQUIRED (if the scene is too dark for still capture).</p> + * + * @see CaptureRequest#CONTROL_AE_LOCK + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureResult#CONTROL_AE_STATE */ public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION = new Key<Integer>("android.control.aeExposureCompensation", int.class); @@ -286,6 +298,8 @@ public final class CaptureResult extends CameraMetadata { * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / * ON_AUTO_FLASH_REDEYE.</p> + * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock + * is ON, the camera device will still adjust its exposure value.</p> * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) * when AE is already locked, the camera device will not change the exposure time * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}) @@ -294,6 +308,7 @@ public final class CaptureResult extends CameraMetadata { * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p> * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p> * + * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index cec90cd..e58c54d 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -156,6 +156,9 @@ public abstract class DisplayManagerInternal { // If true, enables automatic brightness control. public boolean useAutoBrightness; + //If true, scales the brightness to half of desired. + public boolean lowPowerMode; + // If true, prevents the screen from completely turning on if it is currently off. // The display does not enter a "ready" state if this flag is true and screen on is // blocked. The window manager policy blocks screen on while it prepares the keyguard to @@ -203,6 +206,7 @@ public abstract class DisplayManagerInternal { screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; useAutoBrightness = other.useAutoBrightness; blockScreenOn = other.blockScreenOn; + lowPowerMode = other.lowPowerMode; } @Override @@ -218,7 +222,8 @@ public abstract class DisplayManagerInternal { && screenBrightness == other.screenBrightness && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment && useAutoBrightness == other.useAutoBrightness - && blockScreenOn == other.blockScreenOn; + && blockScreenOn == other.blockScreenOn + && lowPowerMode == other.lowPowerMode; } @Override @@ -233,7 +238,8 @@ public abstract class DisplayManagerInternal { + ", screenBrightness=" + screenBrightness + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment + ", useAutoBrightness=" + useAutoBrightness - + ", blockScreenOn=" + blockScreenOn; + + ", blockScreenOn=" + blockScreenOn + + ", lowPowerMode=" + lowPowerMode; } } diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 8437228..ed223d1 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -36,6 +36,7 @@ import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.InputMethodSession; +import android.view.inputmethod.CursorAnchorInfo; class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { @@ -46,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_UPDATE_EXTRACTED_TEXT = 67; private static final int DO_UPDATE_SELECTION = 90; private static final int DO_UPDATE_CURSOR = 95; + private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99; private static final int DO_APP_PRIVATE_COMMAND = 100; private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; @@ -108,6 +110,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.updateCursor((Rect)msg.obj); return; } + case DO_UPDATE_CURSOR_ANCHOR_INFO: { + mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj); + return; + } case DO_APP_PRIVATE_COMMAND: { SomeArgs args = (SomeArgs)msg.obj; mInputMethodSession.appPrivateCommand((String)args.arg1, @@ -181,6 +187,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } @Override + public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo)); + } + + @Override public void appPrivateCommand(String action, Bundle data) { mCaller.executeOrSendMessage( mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f6438b4..4bccaf1 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -51,6 +51,7 @@ import android.view.WindowManager; import android.view.WindowManager.BadTokenException; import android.view.animation.AnimationUtils; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -545,6 +546,17 @@ public class InputMethodService extends AbstractInputMethodService { public void toggleSoftInput(int showFlags, int hideFlags) { InputMethodService.this.onToggleSoftInput(showFlags, hideFlags); } + + /** + * Call {@link InputMethodService#onUpdateCursorAnchorInfo + * InputMethodService.onUpdateCursorAnchorInfo()}. + */ + public void updateCursorAnchorInfo(CursorAnchorInfo info) { + if (!isEnabled()) { + return; + } + InputMethodService.this.onUpdateCursorAnchorInfo(info); + } } /** @@ -1717,6 +1729,17 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Called when the application has reported a new location of its text insertion point and + * characters in the composition string. This is only called if explicitly requested by the + * input method. The default implementation does nothing. + * @param cursorAnchorInfo The positional information of the text insertion point and the + * composition string. + */ + public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) { + // Intentionally empty + } + + /** * Update the cursor/anthor monitor mode. */ public void setCursorAnchorMonitorMode(int monitorMode) { diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index 804f8ee..862d59e 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -45,6 +45,7 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { protected NetworkInfo mNetworkInfo; protected LinkProperties mLinkProperties; protected LinkCapabilities mLinkCapabilities; + protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID); private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); @@ -201,4 +202,14 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { // nothing to do } + + @Override + public void setNetId(int netId) { + mNetwork = new Network(netId); + } + + @Override + public Network getNetwork() { + return mNetwork; + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 30d7043..3e00250 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -408,6 +408,11 @@ public class ConnectivityManager { */ public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000; + /** + * @hide + */ + public final static int INVALID_NET_ID = 0; + private final IConnectivityManager mService; private final String mPackageName; diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/android/net/Network.aidl index 8305271..73ba1af 100644 --- a/core/java/com/android/internal/os/PkgUsageStats.aidl +++ b/core/java/android/net/Network.aidl @@ -1,20 +1,20 @@ -/* //device/java/android/android/content/Intent.aidl +/* ** -** Copyright 2007, The Android Open Source Project +** 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 +** 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 +** 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 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ -package com.android.internal.os; +package android.net; -parcelable PkgUsageStats; +parcelable Network; diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java new file mode 100644 index 0000000..f82bc22 --- /dev/null +++ b/core/java/android/net/Network.java @@ -0,0 +1,59 @@ +/* + * 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.net; + +import android.os.Parcelable; +import android.os.Parcel; + + +/** + * Identifies the Network. + * @hide + */ +public class Network implements Parcelable { + + public final int netId; + + public Network(int netId) { + this.netId = netId; + } + + public Network(Network that) { + this.netId = that.netId; + } + + // implement the Parcelable interface + public int describeContents() { + return 0; + } + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(netId); + } + + public static final Creator<Network> CREATOR = + new Creator<Network>() { + public Network createFromParcel(Parcel in) { + int netId = in.readInt(); + + return new Network(netId); + } + + public Network[] newArray(int size) { + return new Network[size]; + } + }; +} diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index c49b1d1..29b57a5 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -250,4 +250,14 @@ public interface NetworkStateTracker { */ public void stopSampling(SamplingDataTracker.SamplingSnapshot s); + /* + * Record the current netId + */ + public void setNetId(int netId); + + /* + * ? + */ + public Network getNetwork(); + } diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index 8f41e85..daf0065 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -278,7 +278,9 @@ public final class Proxy { host = p.getHost(); port = Integer.toString(p.getPort()); exclList = p.getExclusionListAsString(); - pacFileUrl = p.getPacFileUrl().toString(); + if (p.getPacFileUrl() != null) { + pacFileUrl = p.getPacFileUrl().toString(); + } } setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl index ae9796b..521f4fd 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -34,4 +34,6 @@ interface INfcCardEmulation AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category); boolean removeAidGroupForService(int userHandle, in ComponentName service, String category); List<ApduServiceInfo> getServices(int userHandle, in String category); + boolean setPreferredService(in ComponentName service); + boolean unsetPreferredService(); } diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2820f40..b0449224 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -12,10 +12,15 @@ import android.os.Parcelable; import android.util.Log; /** - * The AidGroup class represents a group of ISO/IEC 7816-4 - * Application Identifiers (AIDs) for a specific application - * category, along with a description resource describing - * the group. + * The AidGroup class represents a group of Application Identifiers (AIDs). + * + * <p>An instance of this object can be used with + * {@link CardEmulation#registerAidGroupForService(android.content.ComponentName, AidGroup)} + * to tell the OS which AIDs are handled by your HCE- or SE-based service. + * + * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class + * requires the AIDs to be input as a hexadecimal string, with an even amount of + * hexadecimal characters, e.g. "F014811481". */ public final class AidGroup implements Parcelable { /** @@ -33,7 +38,7 @@ public final class AidGroup implements Parcelable { * Creates a new AidGroup object. * * @param aids The list of AIDs present in the group - * @param category The category of this group + * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} */ public AidGroup(ArrayList<String> aids, String category) { if (aids == null || aids.size() == 0) { @@ -42,11 +47,12 @@ public final class AidGroup implements Parcelable { if (aids.size() > MAX_NUM_AIDS) { throw new IllegalArgumentException("Too many AIDs in AID group."); } - if (!isValidCategory(category)) { - throw new IllegalArgumentException("Category specified is not valid."); + if (isValidCategory(category)) { + this.category = category; + } else { + this.category = CardEmulation.CATEGORY_OTHER; } this.aids = aids; - this.category = category; this.description = null; } @@ -158,7 +164,7 @@ public final class AidGroup implements Parcelable { } } - boolean isValidCategory(String category) { + static boolean isValidCategory(String category) { return CardEmulation.CATEGORY_PAYMENT.equals(category) || CardEmulation.CATEGORY_OTHER.equals(category); } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 94f35ed..f379ee8 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -290,6 +290,20 @@ public final class ApduServiceInfo implements Parcelable { return groups; } + /** + * Returns the category to which this service has attributed the AID that is passed in, + * or null if we don't know this AID. + */ + public String getCategoryForAid(String aid) { + ArrayList<AidGroup> groups = getAidGroups(); + for (AidGroup group : groups) { + if (group.aids.contains(aid)) { + return group.category; + } + } + return null; + } + public boolean hasCategory(String category) { return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 41f039c..e24a22a 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -18,6 +18,7 @@ package android.nfc.cardemulation; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -28,6 +29,7 @@ import android.nfc.NfcAdapter; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.util.HashMap; @@ -248,6 +250,33 @@ public final class CardEmulation { } /** + * Returns whether the user has allowed AIDs registered in the + * specified category to be handled by a service that is preferred + * by the foreground application, instead of by a pre-configured default. + * + * Foreground applications can set such preferences using the + * {@link #setPreferredService(Activity, ComponentName)} method. + * + * @param category The category, e.g. {@link #CATEGORY_PAYMENT} + * @return whether AIDs in the category can be handled by a service + * specified by the foreground app. + */ + public boolean categoryAllowsForegroundPreference(String category) { + if (CATEGORY_PAYMENT.equals(category)) { + boolean preferForeground = false; + try { + preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; + } catch (SettingNotFoundException e) { + } + return preferForeground; + } else { + // Allowed for all other categories + return true; + } + } + + /** * Returns the service selection mode for the passed in category. * Valid return values are: * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default @@ -269,7 +298,6 @@ public final class CardEmulation { return SELECTION_MODE_ALWAYS_ASK; } } else { - // All other categories are in "only ask if conflict" mode return SELECTION_MODE_ASK_IF_CONFLICT; } } @@ -283,7 +311,7 @@ public final class CardEmulation { * that AID group will be replaced with this one. * * <p>Note that you can only register AIDs for a service that - * is running under the same UID as you are. Typically + * is running under the same UID as the caller of this API. Typically * this means you need to call this from the same * package as the service itself, though UIDs can also * be shared between packages using shared UIDs. @@ -352,7 +380,7 @@ public final class CardEmulation { * method. It will *not* remove AID groups that were statically registered in * the manifest. If a dynamically registered AID group is removed using * this method, and a statically registered AID group for the same category - * exists in the manifest, that AID group will become active again. + * exists in the manifest, the static AID group will become active again. * * @param service The component name of the service * @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT} @@ -378,6 +406,96 @@ public final class CardEmulation { } /** + * Allows a foreground application to specify which card emulation service + * should be preferred while a specific Activity is in the foreground. + * + * <p>The specified Activity must currently be in resumed state. A good + * paradigm is to call this method in your {@link Activity#onResume}, and to call + * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}. + * + * <p>This method call will fail in two specific scenarios: + * <ul> + * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT} + * category, but the user has indicated that foreground apps are not allowed + * to override the default payment service. + * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER} + * category that are also handled by the default payment service, and the + * user has indicated that foreground apps are not allowed to override the + * default payment service. + * </ul> + * + * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine + * whether foreground apps can override the default payment service. + * + * <p>Note that this preference is not persisted by the OS, and hence must be + * called every time the Activity is resumed. + * + * @param activity The activity which prefers this service to be invoked + * @param service The service to be preferred while this activity is in the foreground + * @return whether the registration was successful + */ + public boolean setPreferredService(Activity activity, ComponentName service) { + // Verify the activity is in the foreground before calling into NfcService + if (activity == null || service == null) { + throw new NullPointerException("activity or service or category is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.setPreferredService(service); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setPreferredService(service); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * Unsets the preferred service for the specified Activity. + * + * <p>Note that the specified Activity must still be in resumed + * state at the time of this call. A good place to call this method + * is in your {@link Activity#onPause} implementation. + * + * @param activity The activity which the service was registered for + * @return true when successful + */ + public boolean unsetPreferredService(Activity activity) { + if (activity == null) { + throw new NullPointerException("activity is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.unsetPreferredService(); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.unsetPreferredService(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** * @hide */ public boolean setDefaultServiceForCategory(ComponentName service, String category) { diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e78ce33..4857533 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Formatter; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,6 +31,8 @@ import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.Printer; import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.TimeUtils; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -135,7 +138,7 @@ public abstract class BatteryStats implements Parcelable { /** * Bump the version on this if the checkin format changes. */ - private static final int BATTERY_STATS_CHECKIN_VERSION = 7; + private static final int BATTERY_STATS_CHECKIN_VERSION = 8; private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 @@ -537,6 +540,7 @@ public abstract class BatteryStats implements Parcelable { public static final byte CMD_START = 4; public static final byte CMD_CURRENT_TIME = 5; public static final byte CMD_OVERFLOW = 6; + public static final byte CMD_RESET = 7; public byte cmd = CMD_NULL; @@ -620,6 +624,8 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_SYNC = 0x0004; // Number of event types. public static final int EVENT_COUNT = 0x0005; + // Mask to extract out only the type part of the event. + public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START; public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH; @@ -684,7 +690,7 @@ public abstract class BatteryStats implements Parcelable { dest.writeInt(eventCode); eventTag.writeToParcel(dest, flags); } - if (cmd == CMD_CURRENT_TIME) { + if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) { dest.writeLong(currentTime); } } @@ -722,7 +728,7 @@ public abstract class BatteryStats implements Parcelable { eventCode = EVENT_NONE; eventTag = null; } - if (cmd == CMD_CURRENT_TIME) { + if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) { currentTime = src.readLong(); } else { currentTime = 0; @@ -833,7 +839,59 @@ public abstract class BatteryStats implements Parcelable { return true; } } - + + public final static class HistoryEventTracker { + private final HashMap<String, SparseIntArray>[] mActiveEvents + = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT]; + + public boolean updateState(int code, String name, int uid, int poolIdx) { + if ((code&HistoryItem.EVENT_FLAG_START) != 0) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + HashMap<String, SparseIntArray> active = mActiveEvents[idx]; + if (active == null) { + active = new HashMap<String, SparseIntArray>(); + mActiveEvents[idx] = active; + } + SparseIntArray uids = active.get(name); + if (uids == null) { + uids = new SparseIntArray(); + active.put(name, uids); + } + if (uids.indexOfKey(uid) >= 0) { + // Already set, nothing to do! + return false; + } + uids.put(uid, poolIdx); + } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + HashMap<String, SparseIntArray> active = mActiveEvents[idx]; + if (active == null) { + // not currently active, nothing to do. + return false; + } + SparseIntArray uids = active.get(name); + if (uids == null) { + // not currently active, nothing to do. + return false; + } + idx = uids.indexOfKey(uid); + if (idx < 0) { + // not currently active, nothing to do. + return false; + } + uids.removeAt(idx); + if (uids.size() <= 0) { + active.remove(name); + } + } + return true; + } + + public HashMap<String, SparseIntArray> getStateForEvent(int code) { + return mActiveEvents[code]; + } + } + public static final class BitDescription { public final int mask; public final int shift; @@ -861,7 +919,7 @@ public abstract class BatteryStats implements Parcelable { this.shortValues = shortValues; } } - + public abstract int getHistoryTotalSize(); public abstract int getHistoryUsedSize(); @@ -2958,10 +3016,14 @@ public abstract class BatteryStats implements Parcelable { pw.print(":"); } pw.println("START"); - } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { + } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME + || rec.cmd == HistoryItem.CMD_RESET) { if (checkin) { pw.print(":"); } + if (rec.cmd == HistoryItem.CMD_RESET) { + pw.print("RESET:"); + } pw.print("TIME:"); if (checkin) { pw.println(rec.currentTime); @@ -3187,6 +3249,86 @@ public abstract class BatteryStats implements Parcelable { public static final int DUMP_INCLUDE_HISTORY = 1<<3; public static final int DUMP_VERBOSE = 1<<4; + private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) { + final HistoryPrinter hprinter = new HistoryPrinter(); + final HistoryItem rec = new HistoryItem(); + long lastTime = -1; + long baseTime = -1; + boolean printed = false; + HistoryEventTracker tracker = null; + while (getNextHistoryLocked(rec)) { + lastTime = rec.time; + if (baseTime < 0) { + baseTime = lastTime; + } + if (rec.time >= histStart) { + if (histStart >= 0 && !printed) { + if (rec.cmd == HistoryItem.CMD_CURRENT_TIME + || rec.cmd == HistoryItem.CMD_RESET) { + printed = true; + } else if (rec.currentTime != 0) { + printed = true; + byte cmd = rec.cmd; + rec.cmd = HistoryItem.CMD_CURRENT_TIME; + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = cmd; + } + if (tracker != null) { + int oldCode = rec.eventCode; + HistoryTag oldTag = rec.eventTag; + rec.eventTag = new HistoryTag(); + for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { + HashMap<String, SparseIntArray> active + = tracker.getStateForEvent(i); + if (active == null) { + continue; + } + for (HashMap.Entry<String, SparseIntArray> ent + : active.entrySet()) { + SparseIntArray uids = ent.getValue(); + for (int j=0; j<uids.size(); j++) { + rec.eventCode = i; + rec.eventTag.string = ent.getKey(); + rec.eventTag.uid = uids.keyAt(j); + rec.eventTag.poolIdx = uids.valueAt(j); + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + } + } + } + rec.eventCode = oldCode; + rec.eventTag = oldTag; + tracker = null; + } + } + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + } else if (rec.eventCode != HistoryItem.EVENT_NONE) { + if (tracker == null) { + tracker = new HistoryEventTracker(); + } + tracker.updateState(rec.eventCode, rec.eventTag.string, + rec.eventTag.uid, rec.eventTag.poolIdx); + } + } + if (histStart >= 0) { + pw.print(checkin ? "NEXT: " : " NEXT: "); pw.println(lastTime+1); + } + } + /** * Dumps a human-readable summary of the battery statistics to the given PrintWriter. * @@ -3200,9 +3342,6 @@ public abstract class BatteryStats implements Parcelable { (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0; if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) { - long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); - - final HistoryItem rec = new HistoryItem(); final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); if (startIteratingHistoryLocked()) { @@ -3218,35 +3357,7 @@ public abstract class BatteryStats implements Parcelable { pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); - HistoryPrinter hprinter = new HistoryPrinter(); - long lastTime = -1; - long baseTime = -1; - boolean printed = false; - while (getNextHistoryLocked(rec)) { - lastTime = rec.time; - if (baseTime < 0) { - baseTime = lastTime; - } - if (rec.time >= histStart) { - if (histStart >= 0 && !printed) { - if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { - printed = true; - } else if (rec.currentTime != 0) { - printed = true; - byte cmd = rec.cmd; - rec.cmd = HistoryItem.CMD_CURRENT_TIME; - hprinter.printNextItem(pw, rec, baseTime, false, - (flags&DUMP_VERBOSE) != 0); - rec.cmd = cmd; - } - } - hprinter.printNextItem(pw, rec, baseTime, false, - (flags&DUMP_VERBOSE) != 0); - } - } - if (histStart >= 0) { - pw.print(" NEXT: "); pw.println(lastTime+1); - } + dumpHistoryLocked(pw, flags, histStart, false); pw.println(); } finally { finishIteratingHistoryLocked(); @@ -3255,6 +3366,7 @@ public abstract class BatteryStats implements Parcelable { if (startIteratingOldHistoryLocked()) { try { + final HistoryItem rec = new HistoryItem(); pw.println("Old battery History:"); HistoryPrinter hprinter = new HistoryPrinter(); long baseTime = -1; @@ -3348,7 +3460,6 @@ public abstract class BatteryStats implements Parcelable { (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0; if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) { - final HistoryItem rec = new HistoryItem(); if (startIteratingHistoryLocked()) { try { for (int i=0; i<getHistoryStringPoolSize(); i++) { @@ -3365,37 +3476,7 @@ public abstract class BatteryStats implements Parcelable { pw.print("\""); pw.println(); } - HistoryPrinter hprinter = new HistoryPrinter(); - long lastTime = -1; - long baseTime = -1; - boolean printed = false; - while (getNextHistoryLocked(rec)) { - lastTime = rec.time; - if (baseTime < 0) { - baseTime = lastTime; - } - if (rec.time >= histStart) { - if (histStart >= 0 && !printed) { - if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { - printed = true; - } else if (rec.currentTime != 0) { - printed = true; - byte cmd = rec.cmd; - rec.cmd = HistoryItem.CMD_CURRENT_TIME; - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - hprinter.printNextItem(pw, rec, baseTime, true, false); - rec.cmd = cmd; - } - } - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - hprinter.printNextItem(pw, rec, baseTime, true, false); - } - } - if (histStart >= 0) { - pw.print("NEXT: "); pw.println(lastTime+1); - } + dumpHistoryLocked(pw, flags, histStart, true); } finally { finishIteratingHistoryLocked(); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index f5ff185..9e03f95 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -455,4 +455,14 @@ interface INetworkManagementService * Check whether the mobile radio is currently active. */ boolean isNetworkActive(); + + /** + * setup a new network + */ + void createNetwork(int netId, String iface); + + /** + * remove a network + */ + void removeNetwork(int netId); } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 899a958..cd47099 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -36,6 +36,7 @@ interface IUserManager { Bitmap getUserIcon(int userHandle); List<UserInfo> getUsers(boolean excludeDying); List<UserInfo> getProfiles(int userHandle, boolean enabledOnly); + UserInfo getProfileParent(int userHandle); UserInfo getUserInfo(int userHandle); boolean isRestricted(); void setGuestEnabled(boolean enable); diff --git a/core/java/android/os/ParcelableParcel.aidl b/core/java/android/os/ParcelableParcel.aidl new file mode 100644 index 0000000..61f730c --- /dev/null +++ b/core/java/android/os/ParcelableParcel.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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.os; + +parcelable ParcelableParcel; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e379621..312cdbe 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -301,7 +301,8 @@ public class UserManager { } /** - * Returns the user handle for the user that this application is running for. + * Returns the user handle for the user that the calling process is running on. + * * @return the user handle of the user making this call. * @hide */ @@ -617,7 +618,8 @@ public class UserManager { } /** - * Returns a list of UserHandles for profiles associated with this user, including this user. + * Returns a list of UserHandles for profiles associated with the user that the calling process + * is running on, including the user itself. * * @return A non-empty list of UserHandles associated with the calling user. */ @@ -638,6 +640,21 @@ public class UserManager { } /** + * Returns the parent of the profile which this method is called from + * or null if called from a user that is not a profile. + * + * @hide + */ + public UserInfo getProfileParent(int userHandle) { + try { + return mService.getProfileParent(userHandle); + } catch (RemoteException re) { + Log.w(TAG, "Could not get profile parent", re); + return null; + } + } + + /** * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a badged copy of the given * icon to be able to distinguish it from the original icon. @@ -664,7 +681,7 @@ public class UserManager { private int getBadgeResIdForUser(int userHandle) { // Return the framework-provided badge. - List<UserInfo> userProfiles = getProfiles(UserHandle.myUserId()); + List<UserInfo> userProfiles = getProfiles(getUserHandle()); for (UserInfo user : userProfiles) { if (user.id == userHandle && user.isManagedProfile()) { diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index e4f73cb..811751d 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -167,7 +167,7 @@ public final class PrintManager { /** * Callback notifying that a print job state changed. - * + * * @param printJobId The print job id. */ public void onPrintJobStateChanged(PrintJobId printJobId); @@ -175,7 +175,7 @@ public final class PrintManager { /** * Creates a new instance. - * + * * @param context The current context in which to operate. * @param service The backing system service. * @hide @@ -207,13 +207,17 @@ public final class PrintManager { /** * Creates an instance that can access all print jobs. - * + * * @param userId The user id for which to get all print jobs. * @return An instance if the caller has the permission to access all print * jobs, null otherwise. * @hide */ public PrintManager getGlobalPrintManagerForUser(int userId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrintManager(mContext, mService, userId, APP_ID_ANY); } @@ -228,11 +232,15 @@ public final class PrintManager { /** * Adds a listener for observing the state of print jobs. - * + * * @param listener The listener to add. * @hide */ public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>(); @@ -249,11 +257,15 @@ public final class PrintManager { /** * Removes a listener for observing the state of print jobs. - * + * * @param listener The listener to remove. * @hide */ public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { return; } @@ -275,12 +287,16 @@ public final class PrintManager { /** * Gets a print job given its id. - * + * * @return The print job list. * @see PrintJob * @hide */ public PrintJob getPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } try { PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId); if (printJob != null) { @@ -294,11 +310,15 @@ public final class PrintManager { /** * Gets the print jobs for this application. - * + * * @return The print job list. * @see PrintJob */ public List<PrintJob> getPrintJobs() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); if (printJobInfos == null) { @@ -317,6 +337,10 @@ public final class PrintManager { } void cancelPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.cancelPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -325,6 +349,10 @@ public final class PrintManager { } void restartPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.restartPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -383,6 +411,10 @@ public final class PrintManager { */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } if (!(mContext instanceof Activity)) { throw new IllegalStateException("Can print only from an activity"); } @@ -418,11 +450,15 @@ public final class PrintManager { /** * Gets the list of enabled print services. - * + * * @return The enabled service list or an empty list. * @hide */ public List<PrintServiceInfo> getEnabledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId); if (enabledServices != null) { @@ -436,11 +472,15 @@ public final class PrintManager { /** * Gets the list of installed print services. - * + * * @return The installed service list or an empty list. * @hide */ public List<PrintServiceInfo> getInstalledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId); if (installedServices != null) { @@ -456,6 +496,10 @@ public final class PrintManager { * @hide */ public PrinterDiscoverySession createPrinterDiscoverySession() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrinterDiscoverySession(mService, mContext, mUserId); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dc618c8..1847b55 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4529,6 +4529,12 @@ public final class Settings { public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; /** + * Whether NFC payment is handled by the foreground application or a default. + * @hide + */ + public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground"; + + /** * Specifies the package name currently configured to be the primary sms application * @hide */ diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index d4b29d8..d4919eb 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -17,11 +17,15 @@ package android.service.notification; import android.service.notification.StatusBarNotification; +import android.service.notification.NotificationOrderUpdate; /** @hide */ oneway interface INotificationListener { - void onListenerConnected(in String[] notificationKeys); - void onNotificationPosted(in StatusBarNotification notification); - void onNotificationRemoved(in StatusBarNotification notification); + void onListenerConnected(in NotificationOrderUpdate update); + void onNotificationPosted(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationRemoved(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationOrderUpdate(in NotificationOrderUpdate update); }
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 3673f03..a94f45a 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -22,10 +22,13 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; +import java.util.Comparator; +import java.util.HashMap; + /** * A service that receives calls from the system when new notifications are posted or removed. * <p>To extend this class, you must declare the service in your manifest file with @@ -46,6 +49,7 @@ public abstract class NotificationListenerService extends Service { + "[" + getClass().getSimpleName() + "]"; private INotificationListenerWrapper mWrapper = null; + private String[] mNotificationKeys; private INotificationManager mNoMan; @@ -95,6 +99,15 @@ public abstract class NotificationListenerService extends Service { // optional } + /** + * Implement this method to be notified when the notification order cahnges. + * + * Call {@link #getOrderedNotificationKeys()} to retrieve the new order. + */ + public void onNotificationOrderUpdate() { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -202,7 +215,7 @@ public abstract class NotificationListenerService extends Service { * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { return getActiveNotifications(null /*all*/); @@ -213,7 +226,8 @@ public abstract class NotificationListenerService extends Service { * current user). Useful when you don't know what's already been posted. * * @param keys A specific list of notification keys, or {@code null} for all. - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order + * if {@code keys} is {@code null}. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { if (!isBound()) return null; @@ -226,21 +240,15 @@ public abstract class NotificationListenerService extends Service { } /** - * Request the list of outstanding notification keys(that is, those that are visible to the - * current user). You can use the notification keys for subsequent retrieval via + * Request the list of notification keys in their current natural order. + * You can use the notification keys for subsequent retrieval via * {@link #getActiveNotifications(String[]) or dismissal via * {@link #cancelNotifications(String[]). * - * @return An array of active notification keys. + * @return An array of active notification keys, in their natural order. */ - public String[] getActiveNotificationKeys() { - if (!isBound()) return null; - try { - return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - } - return null; + public String[] getOrderedNotificationKeys() { + return mNotificationKeys; } @Override @@ -261,28 +269,60 @@ public abstract class NotificationListenerService extends Service { private class INotificationListenerWrapper extends INotificationListener.Stub { @Override - public void onNotificationPosted(StatusBarNotification sbn) { + public void onNotificationPosted(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationPosted(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationPosted(sbn); + } } catch (Throwable t) { - Log.w(TAG, "Error running onNotificationPosted", t); + Log.w(TAG, "Error running onOrderedNotificationPosted", t); } } @Override - public void onNotificationRemoved(StatusBarNotification sbn) { + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationRemoved(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationRemoved(sbn); + } } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRemoved", t); } } @Override - public void onListenerConnected(String[] notificationKeys) { + public void onListenerConnected(NotificationOrderUpdate update) { try { - NotificationListenerService.this.onListenerConnected(notificationKeys); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onListenerConnected(mNotificationKeys); + } } catch (Throwable t) { Log.w(TAG, "Error running onListenerConnected", t); } } + @Override + public void onNotificationOrderUpdate(NotificationOrderUpdate update) + throws RemoteException { + try { + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationOrderUpdate(); + } + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationOrderUpdate", t); + } + } + } + + private void updateNotificationKeys(NotificationOrderUpdate update) { + // TODO: avoid garbage by comparing the lists + mNotificationKeys = update.getOrderedKeys(); } } diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl new file mode 100644 index 0000000..5d50641 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.aidl @@ -0,0 +1,19 @@ +/** + * 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.service.notification; + +parcelable NotificationOrderUpdate; diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java new file mode 100644 index 0000000..20e19a3 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.java @@ -0,0 +1,62 @@ +/* + * 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.service.notification; + +import android.os.Parcel; +import android.os.Parcelable; + +public class NotificationOrderUpdate implements Parcelable { + // TODO replace this with an update instead of the whole array + private final String[] mKeys; + + /** @hide */ + public NotificationOrderUpdate(String[] keys) { + this.mKeys = keys; + } + + public NotificationOrderUpdate(Parcel in) { + this.mKeys = in.readStringArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeStringArray(this.mKeys); + } + + public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR + = new Parcelable.Creator<NotificationOrderUpdate>() { + public NotificationOrderUpdate createFromParcel(Parcel parcel) { + return new NotificationOrderUpdate(parcel); + } + + public NotificationOrderUpdate[] newArray(int size) { + return new NotificationOrderUpdate[size]; + } + }; + + /** + * @hide + * @return ordered list of keys + */ + String[] getOrderedKeys() { + return mKeys; + } +} diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 05e202b..2d1016a 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1685,10 +1685,6 @@ public class KeyEvent extends InputEvent implements Parcelable { case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: case KeyEvent.KEYCODE_BRIGHTNESS_UP: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_LEFT: return true; } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index a675821..be3b6ce 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -21,6 +21,8 @@ import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; +import com.android.internal.util.VirtualRefBasePtr; + import java.lang.ref.WeakReference; /** @@ -70,28 +72,32 @@ public final class RenderNodeAnimator { public static final int DELTA_TYPE_DELTA = 1; private RenderNode mTarget; - private long mNativePtr; + private VirtualRefBasePtr mNativePtr; public int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } public RenderNodeAnimator(int property, int deltaType, float deltaValue) { - mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), - property, deltaType, deltaValue); + init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), + property, deltaType, deltaValue)); } public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) { - mNativePtr = nCreateCanvasPropertyFloatAnimator( + init(nCreateCanvasPropertyFloatAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), deltaType, deltaValue); + property.getNativeContainer(), deltaType, deltaValue)); } public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, int deltaType, float deltaValue) { - mNativePtr = nCreateCanvasPropertyPaintAnimator( + init(nCreateCanvasPropertyPaintAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), paintField, deltaType, deltaValue); + property.getNativeContainer(), paintField, deltaType, deltaValue)); + } + + private void init(long ptr) { + mNativePtr = new VirtualRefBasePtr(ptr); } public void start(View target) { @@ -115,11 +121,11 @@ public final class RenderNodeAnimator { } public void setDuration(int duration) { - nSetDuration(mNativePtr, duration); + nSetDuration(mNativePtr.get(), duration); } long getNativeAnimator() { - return mNativePtr; + return mNativePtr.get(); } private void onFinished() { @@ -134,16 +140,6 @@ public final class RenderNodeAnimator { } } - @Override - protected void finalize() throws Throwable { - try { - nUnref(mNativePtr); - mNativePtr = 0; - } finally { - super.finalize(); - } - } - private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, int property, int deltaValueType, float deltaValue); private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, @@ -151,5 +147,4 @@ public final class RenderNodeAnimator { private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, long canvasProperty, int paintField, int deltaValueType, float deltaValue); private static native void nSetDuration(long nativePtr, int duration); - private static native void nUnref(long nativePtr); } diff --git a/core/java/android/view/inputmethod/CorrectionInfo.java b/core/java/android/view/inputmethod/CorrectionInfo.java index 1b04e49..a43dfe8 100644 --- a/core/java/android/view/inputmethod/CorrectionInfo.java +++ b/core/java/android/view/inputmethod/CorrectionInfo.java @@ -88,16 +88,15 @@ public final class CorrectionInfo implements Parcelable { /** * Used to make this class parcelable. */ - public static final Parcelable.Creator<CorrectionInfo> CREATOR - = new Parcelable.Creator<CorrectionInfo>() { - public CorrectionInfo createFromParcel(Parcel source) { - return new CorrectionInfo(source); - } - - public CorrectionInfo[] newArray(int size) { - return new CorrectionInfo[size]; - } - }; + public static final Parcelable.Creator<CorrectionInfo> CREATOR = + new Parcelable.Creator<CorrectionInfo>() { + public CorrectionInfo createFromParcel(Parcel source) { + return new CorrectionInfo(source); + } + public CorrectionInfo[] newArray(int size) { + return new CorrectionInfo[size]; + } + }; public int describeContents() { return 0; diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.aidl b/core/java/android/view/inputmethod/CursorAnchorInfo.aidl new file mode 100644 index 0000000..2ee9edb --- /dev/null +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.view.inputmethod; + +parcelable CursorAnchorInfo; diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java new file mode 100644 index 0000000..92455df --- /dev/null +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -0,0 +1,449 @@ +/* + * 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.view.inputmethod; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.Layout; +import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder; + +import java.util.Objects; + +/** + * Positional information about the text insertion point and characters in the composition string. + * + * <p>This class encapsulates locations of the text insertion point and the composition string in + * the screen coordinates so that IMEs can render their UI components near where the text is + * actually inserted.</p> + */ +public final class CursorAnchorInfo implements Parcelable { + private final int mSelectionStart; + private final int mSelectionEnd; + private final int mCandidatesStart; + private final int mCandidatesEnd; + + /** + * Horizontal position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerHorizontal; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineTop(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerTop; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerBaseline; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineBottom(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerBottom; + + /** + * Container of rectangular position of characters, keyed with character index in a unit of + * Java chars, in the local coordinates that will be transformed with the transformation matrix + * when rendered on the screen. + */ + private final SparseRectFArray mCharacterRects; + + /** + * Transformation matrix that is applied to any positional information of this class to + * transform local coordinates into screen coordinates. + */ + private final Matrix mMatrix; + + public CursorAnchorInfo(final Parcel source) { + mSelectionStart = source.readInt(); + mSelectionEnd = source.readInt(); + mCandidatesStart = source.readInt(); + mCandidatesEnd = source.readInt(); + mInsertionMarkerHorizontal = source.readFloat(); + mInsertionMarkerTop = source.readFloat(); + mInsertionMarkerBaseline = source.readFloat(); + mInsertionMarkerBottom = source.readFloat(); + mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader()); + mMatrix = new Matrix(); + mMatrix.setValues(source.createFloatArray()); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSelectionStart); + dest.writeInt(mSelectionEnd); + dest.writeInt(mCandidatesStart); + dest.writeInt(mCandidatesEnd); + dest.writeFloat(mInsertionMarkerHorizontal); + dest.writeFloat(mInsertionMarkerTop); + dest.writeFloat(mInsertionMarkerBaseline); + dest.writeFloat(mInsertionMarkerBottom); + dest.writeParcelable(mCharacterRects, flags); + final float[] matrixArray = new float[9]; + mMatrix.getValues(matrixArray); + dest.writeFloatArray(matrixArray); + } + + @Override + public int hashCode(){ + // TODO: Improve the hash function. + final float floatHash = mSelectionStart + mSelectionEnd + mCandidatesStart + mCandidatesEnd + + mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline + + mInsertionMarkerBottom; + int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash); + if (mCharacterRects != null) { + hash += mCharacterRects.hashCode(); + } + hash += mMatrix.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj){ + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof CursorAnchorInfo)) { + return false; + } + final CursorAnchorInfo that = (CursorAnchorInfo) obj; + if (hashCode() != that.hashCode()) { + return false; + } + if (mSelectionStart != that.mSelectionStart + || mSelectionEnd != that.mSelectionEnd + || mCandidatesStart != that.mCandidatesStart + || mCandidatesEnd != that.mCandidatesEnd) { + return false; + } + if (!Objects.equals(mCharacterRects, that.mCharacterRects)) { + return false; + } + if (!Objects.equals(mMatrix, that.mMatrix)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd + + " mCandiadtes=" + mCandidatesStart + "," + mCandidatesEnd + + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal + + " mInsertionMarkerTop=" + mInsertionMarkerTop + + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline + + " mInsertionMarkerBottom=" + mInsertionMarkerBottom + + " mCharacterRects=" + (mCharacterRects != null ? mCharacterRects : "null") + + " mMatrix=" + mMatrix + + "}"; + } + + /** + * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe. + */ + public static final class CursorAnchorInfoBuilder { + /** + * Sets the text range of the selection. Calling this can be skipped if there is no + * selection. + */ + public CursorAnchorInfoBuilder setSelectionRange(final int newStart, final int newEnd) { + mSelectionStart = newStart; + mSelectionEnd = newEnd; + return this; + } + private int mSelectionStart = -1; + private int mSelectionEnd = -1; + + /** + * Sets the text range of the composition string. Calling this can be skipped if there is + * no composition. + */ + public CursorAnchorInfoBuilder setCandidateRange(final int start, final int end) { + mCandidateStart = start; + mCandidateEnd = end; + return this; + } + private int mCandidateStart = -1; + private int mCandidateEnd = -1; + + /** + * Sets the location of the text insertion point (zero width cursor) as a rectangle in + * local coordinates. Calling this can be skipped when there is no text insertion point; + * however if there is an insertion point, editors must call this method. + * @param horizontalPosition horizontal position of the insertion marker, in the local + * coordinates that will be transformed with the transformation matrix when rendered on the + * screen. This should be calculated or compatible with + * {@link Layout#getPrimaryHorizontal(int)}. + * @param lineTop vertical position of the insertion marker, in the local coordinates that + * will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineTop(int)}. + * @param lineBaseline vertical position of the insertion marker, in the local coordinates + * that will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineBaseline(int)}. + * @param lineBottom vertical position of the insertion marker, in the local coordinates + * that will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineBottom(int)}. + */ + public CursorAnchorInfoBuilder setInsertionMarkerLocation( + final float horizontalPosition, final float lineTop, final float lineBaseline, + final float lineBottom){ + mInsertionMarkerHorizontal = horizontalPosition; + mInsertionMarkerTop = lineTop; + mInsertionMarkerBaseline = lineBaseline; + mInsertionMarkerBottom = lineBottom; + return this; + } + private float mInsertionMarkerHorizontal = Float.NaN; + private float mInsertionMarkerTop = Float.NaN; + private float mInsertionMarkerBaseline = Float.NaN; + private float mInsertionMarkerBottom = Float.NaN; + + /** + * Adds the bounding box of the character specified with the index. + * <p> + * Editor authors should not call this method for characters that are invisible. + * </p> + * + * @param index index of the character in Java chars units. Must be specified in + * ascending order across successive calls. + * @param leadingEdgeX x coordinate of the leading edge of the character in local + * coordinates, that is, left edge for LTR text and right edge for RTL text. + * @param leadingEdgeY y coordinate of the leading edge of the character in local + * coordinates. + * @param trailingEdgeX x coordinate of the trailing edge of the character in local + * coordinates, that is, right edge for LTR text and left edge for RTL text. + * @param trailingEdgeY y coordinate of the trailing edge of the character in local + * coordinates. + * @throws IllegalArgumentException If the index is a negative value, or not greater than + * all of the previously called indices. + */ + public CursorAnchorInfoBuilder addCharacterRect(final int index, + final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX, + final float trailingEdgeY) { + if (index < 0) { + throw new IllegalArgumentException("index must not be a negative integer."); + } + if (mCharacterRectBuilder == null) { + mCharacterRectBuilder = new SparseRectFArrayBuilder(); + } + mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX, + trailingEdgeY); + return this; + } + private SparseRectFArrayBuilder mCharacterRectBuilder = null; + + /** + * Sets the matrix that transforms local coordinates into screen coordinates. + * @param matrix transformation matrix from local coordinates into screen coordinates. null + * is interpreted as an identity matrix. + */ + public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) { + if (matrix != null) { + mMatrix = matrix; + } else { + mMatrix = Matrix.IDENTITY_MATRIX; + } + return this; + } + private Matrix mMatrix = Matrix.IDENTITY_MATRIX; + + /** + * @return {@link CursorAnchorInfo} using parameters in this + * {@link CursorAnchorInfoBuilder}. + */ + public CursorAnchorInfo build() { + return new CursorAnchorInfo(this); + } + + /** + * Resets the internal state so that this instance can be reused to build another + * instance of {@link CursorAnchorInfo}. + */ + public void reset() { + mSelectionStart = -1; + mSelectionEnd = -1; + mCandidateStart = -1; + mCandidateEnd = -1; + mInsertionMarkerHorizontal = Float.NaN; + mInsertionMarkerTop = Float.NaN; + mInsertionMarkerBaseline = Float.NaN; + mInsertionMarkerBottom = Float.NaN; + mMatrix = Matrix.IDENTITY_MATRIX; + if (mCharacterRectBuilder != null) { + mCharacterRectBuilder.reset(); + } + } + } + + private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) { + mSelectionStart = builder.mSelectionStart; + mSelectionEnd = builder.mSelectionEnd; + mCandidatesStart = builder.mCandidateStart; + mCandidatesEnd = builder.mCandidateEnd; + mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal; + mInsertionMarkerTop = builder.mInsertionMarkerTop; + mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; + mInsertionMarkerBottom = builder.mInsertionMarkerBottom; + mCharacterRects = builder.mCharacterRectBuilder != null ? + builder.mCharacterRectBuilder.build() : null; + mMatrix = builder.mMatrix; + } + + /** + * Returns the index where the selection starts. + * @return -1 if there is no selection. + */ + public int getSelectionStart() { + return mSelectionStart; + } + + /** + * Returns the index where the selection ends. + * @return -1 if there is no selection. + */ + public int getSelectionEnd() { + return mSelectionEnd; + } + + /** + * Returns the index where the composition starts. + * @return -1 if there is no composition. + */ + public int getCandidatesStart() { + return mCandidatesStart; + } + + /** + * Returns the index where the composition ends. + * @return -1 if there is no composition. + */ + public int getCandidatesEnd() { + return mCandidatesEnd; + } + + /** + * Returns the horizontal start of the insertion marker, in the local coordinates that will + * be transformed with {@link #getMatrix()} when rendered on the screen. + * @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}. + * Pay special care to RTL/LTR handling. + * {@code java.lang.Float.NaN} if not specified. + * @see Layout#getPrimaryHorizontal(int) + */ + public float getInsertionMarkerHorizontal() { + return mInsertionMarkerHorizontal; + } + /** + * Returns the vertical top position of the insertion marker, in the local coordinates that + * will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineTop(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerTop() { + return mInsertionMarkerTop; + } + /** + * Returns the vertical baseline position of the insertion marker, in the local coordinates + * that will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerBaseline() { + return mInsertionMarkerBaseline; + } + /** + * Returns the vertical bottom position of the insertion marker, in the local coordinates + * that will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerBottom() { + return mInsertionMarkerBottom; + } + + /** + * Returns a new instance of {@link RectF} that indicates the location of the character + * specified with the index. + * <p> + * Note that coordinates are not necessarily contiguous or even monotonous, especially when + * RTL text and LTR text are mixed. + * </p> + * @param index index of the character in a Java chars. + * @return a new instance of {@link RectF} that represents the location of the character in + * local coordinates. null if the character is invisible or the application did not provide + * the location. Note that the {@code left} field can be greater than the {@code right} field + * if the character is in RTL text. + */ + // TODO: Prepare a document about the expected behavior for surrogate pairs, combining + // characters, and non-graphical chars. + public RectF getCharacterRect(final int index) { + if (mCharacterRects == null) { + return null; + } + return mCharacterRects.get(index); + } + + /** + * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation + * matrix that is to be applied other positional data in this class. + * @return a new instance (copy) of the transformation matrix. + */ + public Matrix getMatrix() { + return new Matrix(mMatrix); + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<CursorAnchorInfo> CREATOR + = new Parcelable.Creator<CursorAnchorInfo>() { + @Override + public CursorAnchorInfo createFromParcel(Parcel source) { + return new CursorAnchorInfo(source); + } + + @Override + public CursorAnchorInfo[] newArray(int size) { + return new CursorAnchorInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0227873..be0c27d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -321,7 +321,6 @@ public final class InputMethodManager { * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}. */ private final int[] mViewTopLeft = new int[2]; - // ----------------------------------------------------------- /** @@ -1435,7 +1434,7 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } - + if (mCursorSelStart != selStart || mCursorSelEnd != selEnd || mCursorCandStart != candidatesStart || mCursorCandEnd != candidatesEnd) { @@ -1556,6 +1555,31 @@ public final class InputMethodManager { } /** + * Report positional change of the text insertion point and/or characters in the composition + * string. + */ + public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) { + if (view == null || cursorAnchorInfo == null) { + return; + } + checkFocus(); + synchronized (mH) { + if ((mServedView != view && + (mServedView == null || !mServedView.checkInputConnectionProxy(view))) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo"); + + try { + mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + + /** * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) * InputMethodSession.appPrivateCommand()} on the current Input Method. * @param view Optional View that is sending the command, or null if diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index 6386299..74fbbc7 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -165,7 +165,7 @@ public interface InputMethodSession { public void appPrivateCommand(String action, Bundle data); /** - * Toggle the soft input window. + * Toggle the soft input window. * Applications can toggle the state of the soft input window. * @param showFlags Provides additional operating flags. May be * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT}, @@ -175,4 +175,14 @@ public interface InputMethodSession { * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set. */ public void toggleSoftInput(int showFlags, int hideFlags); + + /** + * This method is called when the cursor and/or the character position relevant to text input + * is changed on the screen. This is not called by default. It will only be reported if + * requested by the input method. + * + * @param cursorAnchorInfo Positional information relevant to text input, such as text + * insertion point and composition string. + */ + public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo); } diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java new file mode 100644 index 0000000..40cade7 --- /dev/null +++ b/core/java/android/view/inputmethod/SparseRectFArray.java @@ -0,0 +1,265 @@ +/* + * 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.view.inputmethod; + +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * An implementation of SparseArray specialized for {@link android.graphics.RectF}. + * <p> + * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This + * class could be in some other packages like android.graphics or android.util but currently + * belong to android.view.inputmethod because this class is hidden and used only in input method + * framework. + * </p> + * @hide + */ +public final class SparseRectFArray implements Parcelable { + /** + * The keys, in ascending order, of those {@link RectF} that are not null. For example, + * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}. + * @see #mCoordinates + */ + private final int[] mKeys; + + /** + * Stores coordinates of the rectangles, in the order of + * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top}, + * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom}, + * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top}, + * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom}, + * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, .... + */ + private final float[] mCoordinates; + + public SparseRectFArray(final Parcel source) { + mKeys = source.createIntArray(); + mCoordinates = source.createFloatArray(); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mKeys); + dest.writeFloatArray(mCoordinates); + } + + @Override + public int hashCode() { + // TODO: Improve the hash function. + if (mKeys == null || mKeys.length == 0) { + return 0; + } + int hash = mKeys.length; + // For performance reasons, only the first rectangle is used for the hash code now. + for (int i = 0; i < 4; i++) { + hash *= 31; + hash += mCoordinates[i]; + } + return hash; + } + + @Override + public boolean equals(Object obj){ + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof SparseRectFArray)) { + return false; + } + final SparseRectFArray that = (SparseRectFArray) obj; + + return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates); + } + + @Override + public String toString() { + if (mKeys == null || mCoordinates == null) { + return "SparseRectFArray{}"; + } + final StringBuilder sb = new StringBuilder(); + sb.append("SparseRectFArray{"); + for (int i = 0; i < mKeys.length; i++) { + if (i != 0) { + sb.append(", "); + } + final int baseIndex = i * 4; + sb.append(mKeys[i]); + sb.append(":["); + sb.append(mCoordinates[baseIndex + 0]); + sb.append(","); + sb.append(mCoordinates[baseIndex + 1]); + sb.append("],["); + sb.append(mCoordinates[baseIndex + 2]); + sb.append(","); + sb.append(mCoordinates[baseIndex + 3]); + sb.append("]"); + } + sb.append("}"); + return sb.toString(); + } + + /** + * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe. + * @hide + */ + public static final class SparseRectFArrayBuilder { + /** + * Throws {@link IllegalArgumentException} to make sure that this class is correctly used. + * @param key key to be checked. + */ + private void checkIndex(final int key) { + if (mCount == 0) { + return; + } + if (mKeys[mCount - 1] >= key) { + throw new IllegalArgumentException("key must be greater than all existing keys."); + } + } + + /** + * Extends the internal array if necessary. + */ + private void ensureBufferSize() { + if (mKeys == null) { + mKeys = new int[INITIAL_SIZE]; + } + if (mCoordinates == null) { + mCoordinates = new float[INITIAL_SIZE * 4]; + } + final int requiredIndexArraySize = mCount + 1; + if (mKeys.length <= requiredIndexArraySize) { + final int[] newArray = new int[requiredIndexArraySize * 2]; + System.arraycopy(mKeys, 0, newArray, 0, mCount); + mKeys = newArray; + } + final int requiredCoordinatesArraySize = (mCount + 1) * 4; + if (mCoordinates.length <= requiredCoordinatesArraySize) { + final float[] newArray = new float[requiredCoordinatesArraySize * 2]; + System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4); + mCoordinates = newArray; + } + } + + /** + * Puts the rectangle with an integer key. + * @param key the key to be associated with the rectangle. It must be greater than all + * existing keys that have been previously specified. + * @param left left of the rectangle. + * @param top top of the rectangle. + * @param right right of the rectangle. + * @param bottom bottom of the rectangle. + * @return the receiver object itself for chaining method calls. + * @throws IllegalArgumentException If the index is not greater than all of existing keys. + */ + public SparseRectFArrayBuilder append(final int key, + final float left, final float top, final float right, final float bottom) { + checkIndex(key); + ensureBufferSize(); + final int baseCoordinatesIndex = mCount * 4; + mCoordinates[baseCoordinatesIndex + 0] = left; + mCoordinates[baseCoordinatesIndex + 1] = top; + mCoordinates[baseCoordinatesIndex + 2] = right; + mCoordinates[baseCoordinatesIndex + 3] = bottom; + mKeys[mCount] = key; + ++mCount; + return this; + } + private int mCount = 0; + private int[] mKeys = null; + private float[] mCoordinates = null; + private static int INITIAL_SIZE = 16; + + /** + * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}. + */ + public SparseRectFArray build() { + return new SparseRectFArray(this); + } + + public void reset() { + if (mCount == 0) { + mKeys = null; + mCoordinates = null; + } + mCount = 0; + } + } + + private SparseRectFArray(final SparseRectFArrayBuilder builder) { + if (builder.mCount == 0) { + mKeys = null; + mCoordinates = null; + } else { + mKeys = new int[builder.mCount]; + mCoordinates = new float[builder.mCount * 4]; + System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount); + System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4); + } + } + + public RectF get(final int index) { + if (mKeys == null) { + return null; + } + if (index < 0) { + return null; + } + final int arrayIndex = Arrays.binarySearch(mKeys, index); + if (arrayIndex < 0) { + return null; + } + final int baseCoordIndex = arrayIndex * 4; + return new RectF(mCoordinates[baseCoordIndex], + mCoordinates[baseCoordIndex + 1], + mCoordinates[baseCoordIndex + 2], + mCoordinates[baseCoordIndex + 3]); + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<SparseRectFArray> CREATOR = + new Parcelable.Creator<SparseRectFArray>() { + @Override + public SparseRectFArray createFromParcel(Parcel source) { + return new SparseRectFArray(source); + } + @Override + public SparseRectFArray[] newArray(int size) { + return new SparseRectFArray[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } +} + diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags index b0b5493..a90aebd 100644 --- a/core/java/android/webkit/EventLogTags.logtags +++ b/core/java/android/webkit/EventLogTags.logtags @@ -8,3 +8,4 @@ option java_package android.webkit; # 70103- used by the browser app itself 70150 browser_snap_center +70151 exp_det_attempt_to_call_object_getclass (app_signature|3) diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 3e33498..fa760b7 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -28,6 +28,7 @@ import android.net.Uri; public interface PermissionRequest { /** * Resource belongs to geolocation service. + * @hide - see b/14668406 */ public final static long RESOURCE_GEOLOCATION = 1 << 0; /** diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 25bcd44..ac12357 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -50,28 +50,6 @@ public final class WebViewFactory { private static WebViewFactoryProvider sProviderInstance; private static final Object sProviderLock = new Object(); - public static boolean isExperimentalWebViewAvailable() { - // TODO: Remove callers of this method then remove it. - return false; // Hide the toggle in Developer Settings. - } - - /** @hide */ - public static void setUseExperimentalWebView(boolean enable) { - // TODO: Remove callers of this method then remove it. - } - - /** @hide */ - public static boolean useExperimentalWebView() { - // TODO: Remove callers of this method then remove it. - return true; - } - - /** @hide */ - public static boolean isUseExperimentalWebViewSet() { - // TODO: Remove callers of this method then remove it. - return false; // User has not modifed Developer Settings - } - static WebViewFactoryProvider getProvider() { synchronized (sProviderLock) { // For now the main purpose of this function (and the factory abstraction) is to keep diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index f4cd5fc..d9a4f57 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -743,7 +743,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param view The view whose scroll state is being reported * - * @param scrollState The current scroll state. One of + * @param scrollState The current scroll state. One of * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */ public void onScrollStateChanged(AbsListView view, int scrollState); @@ -3267,7 +3267,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - private boolean startScrollIfNeeded(int y, MotionEvent vtev) { + private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) { // Check if we have moved far enough that it looks more like a // scroll than a tap final int deltaY = y - mMotionY; @@ -3296,27 +3296,31 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } - scrollIfNeeded(y, vtev); + scrollIfNeeded(x, y, vtev); return true; } return false; } - private void scrollIfNeeded(int y, MotionEvent vtev) { + private void scrollIfNeeded(int x, int y, MotionEvent vtev) { int rawDeltaY = y - mMotionY; + int scrollOffsetCorrection = 0; + int scrollConsumedCorrection = 0; + if (mLastY == Integer.MIN_VALUE) { + rawDeltaY -= mMotionCorrection; + } if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) { rawDeltaY -= mScrollConsumed[1]; - mMotionCorrection -= mScrollOffset[1]; - if (mLastY != Integer.MIN_VALUE) { - mLastY -= mScrollOffset[1] + mScrollConsumed[1]; - } + scrollOffsetCorrection -= mScrollOffset[1]; + scrollConsumedCorrection -= mScrollConsumed[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } } - final int deltaY = rawDeltaY - mMotionCorrection; - int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; + final int deltaY = rawDeltaY; + int incrementalDeltaY = + mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; if (mTouchMode == TOUCH_MODE_SCROLL) { @@ -3378,46 +3382,51 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (motionViewRealTop - motionViewPrevTop); if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll, mScrollOffset)) { - mMotionCorrection -= mScrollOffset[1]; lastYCorrection -= mScrollOffset[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } } else { - overScrollBy(0, overscroll, 0, mScrollY, 0, 0, - 0, mOverscrollDistance, true); - if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) { - // Don't allow overfling if we're at the edge. - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } + final boolean atOverscrollEdge = overScrollBy(0, overscroll, + 0, mScrollY, 0, 0, 0, mOverscrollDistance, true); + + if (atOverscrollEdge && mVelocityTracker != null) { + // Don't allow overfling if we're at the edge + mVelocityTracker.clear(); } final int overscrollMode = getOverScrollMode(); if (overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { - mDirection = 0; // Reset when entering overscroll. - mTouchMode = TOUCH_MODE_OVERSCROLL; - if (deltaY > 0) { - mEdgeGlowTop.onPull((float) overscroll / getHeight()); + if (!atOverscrollEdge) { + mDirection = 0; // Reset when entering overscroll. + mTouchMode = TOUCH_MODE_OVERSCROLL; + } + if (incrementalDeltaY > 0) { + mEdgeGlowTop.onPull((float) overscroll / getHeight(), + (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } - invalidate(mEdgeGlowTop.getBounds(false)); - } else if (deltaY < 0) { - mEdgeGlowBottom.onPull((float) overscroll / getHeight()); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); + } else if (incrementalDeltaY < 0) { + mEdgeGlowBottom.onPull((float) overscroll / getHeight(), + 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } } } } - mMotionY = y; + mMotionY = y + scrollOffsetCorrection; } - mLastY = y + lastYCorrection; + mLastY = y + lastYCorrection + scrollOffsetCorrection; } } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) { if (y != mLastY) { @@ -3445,17 +3454,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) overScrollDistance / getHeight()); + mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(), + (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } - invalidate(mEdgeGlowTop.getBounds(false)); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight()); + mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(), + 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } } } @@ -3703,7 +3717,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case TOUCH_MODE_DONE_WAITING: // Check if we have moved far enough that it looks more like a // scroll than a tap. If so, we'll enter scrolling mode. - if (startScrollIfNeeded(y, vtev)) { + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) { break; } // Otherwise, check containment within list bounds. If we're @@ -3723,7 +3737,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; case TOUCH_MODE_SCROLL: case TOUCH_MODE_OVERSCROLL: - scrollIfNeeded(y, vtev); + scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev); break; } } @@ -4022,8 +4036,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te canvas.translate(leftPadding, edgeY); mEdgeGlowTop.setSize(width, getHeight()); if (mEdgeGlowTop.draw(canvas)) { - mEdgeGlowTop.setPosition(leftPadding, edgeY); - invalidate(mEdgeGlowTop.getBounds(false)); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); } canvas.restoreToCount(restoreCount); } @@ -4040,9 +4054,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); if (mEdgeGlowBottom.draw(canvas)) { - // Account for the rotation - mEdgeGlowBottom.setPosition(edgeX + width, edgeY); - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } canvas.restoreToCount(restoreCount); } @@ -4161,7 +4175,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int y = (int) ev.getY(pointerIndex); initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); - if (startScrollIfNeeded(y, null)) { + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { return true; } break; diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 225cd6d..4f2d9c6 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -19,7 +19,9 @@ package android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; @@ -32,9 +34,12 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.R; public abstract class AbsSeekBar extends ProgressBar { + private final Rect mTempRect = new Rect(); + private Drawable mThumb; private int mThumbOffset; - + private boolean mSplitTrack; + /** * On touch, this offset plus the scaled value from the position of the * touch will form the progress value. Usually 0. @@ -51,10 +56,10 @@ public abstract class AbsSeekBar extends ProgressBar { * progress. */ private int mKeyProgressIncrement = 1; - + private static final int NO_ALPHA = 0xFF; private float mDisabledAlpha; - + private int mScaledTouchSlop; private float mTouchDownX; private boolean mIsDragging; @@ -76,12 +81,16 @@ public abstract class AbsSeekBar extends ProgressBar { TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes); - Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); - setThumb(thumb); // will guess mThumbOffset if thumb != null... - // ...but allow layout to override this - int thumbOffset = a.getDimensionPixelOffset( + + final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); + setThumb(thumb); + + // Guess thumb offset if thumb != null, but allow layout to override. + final int thumbOffset = a.getDimensionPixelOffset( com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); setThumbOffset(thumbOffset); + + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false); a.recycle(); a = context.obtainStyledAttributes(attrs, @@ -97,7 +106,7 @@ public abstract class AbsSeekBar extends ProgressBar { * <p> * If the thumb is a valid drawable (i.e. not null), half its width will be * used as the new thumb offset (@see #setThumbOffset(int)). - * + * * @param thumb Drawable representing the thumb */ public void setThumb(Drawable thumb) { @@ -132,7 +141,7 @@ public abstract class AbsSeekBar extends ProgressBar { mThumb = thumb; invalidate(); if (needUpdate) { - updateThumbPos(getWidth(), getHeight()); + updateThumbAndTrackPos(getWidth(), getHeight()); if (thumb != null && thumb.isStateful()) { // Note that if the states are different this won't work. // For now, let's consider that an app bug. @@ -162,7 +171,7 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Sets the thumb offset that allows the thumb to extend out of the range of * the track. - * + * * @param thumbOffset The offset amount in pixels. */ public void setThumbOffset(int thumbOffset) { @@ -171,8 +180,27 @@ public abstract class AbsSeekBar extends ProgressBar { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Sets the amount of progress changed via the arrow keys. - * + * * @param increment The amount to increment or decrement when the user * presses the arrow keys. */ @@ -184,14 +212,14 @@ public abstract class AbsSeekBar extends ProgressBar { * Returns the amount of progress changed via the arrow keys. * <p> * By default, this will be a value that is derived from the max progress. - * + * * @return The amount to increment or decrement when the user presses the * arrow keys. This will be positive. */ public int getKeyProgressIncrement() { return mKeyProgressIncrement; } - + @Override public synchronized void setMax(int max) { super.setMax(max); @@ -217,79 +245,85 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected void drawableStateChanged() { super.drawableStateChanged(); - - Drawable progressDrawable = getProgressDrawable(); + + final Drawable progressDrawable = getProgressDrawable(); if (progressDrawable != null) { progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha)); } - - if (mThumb != null && mThumb.isStateful()) { - int[] state = getDrawableState(); - mThumb.setState(state); + + final Drawable thumb = mThumb; + if (thumb != null && thumb.isStateful()) { + thumb.setState(getDrawableState()); } } - + @Override void onProgressRefresh(float scale, boolean fromUser) { super.onProgressRefresh(scale, fromUser); - Drawable thumb = mThumb; + + final Drawable thumb = mThumb; if (thumb != null) { setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } - - + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - updateThumbPos(w, h); + + updateThumbAndTrackPos(w, h); } - private void updateThumbPos(int w, int h) { - Drawable d = getCurrentDrawable(); - Drawable thumb = mThumb; - int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + private void updateThumbAndTrackPos(int w, int h) { + final Drawable track = getCurrentDrawable(); + final Drawable thumb = mThumb; + // The max height does not incorporate padding, whereas the height - // parameter does - int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); - - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - + // parameter does. + final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); + final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + + // Apply offset to whichever item is taller. + final int trackOffset; + final int thumbOffset; if (thumbHeight > trackHeight) { - if (thumb != null) { - setThumbPos(w, thumb, scale, 0); - } - int gapForCenteringTrack = (thumbHeight - trackHeight) / 2; - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, gapForCenteringTrack, - w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack - - mPaddingTop); - } + trackOffset = (thumbHeight - trackHeight) / 2; + thumbOffset = 0; } else { - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - - mPaddingTop); - } - int gap = (trackHeight - thumbHeight) / 2; - if (thumb != null) { - setThumbPos(w, thumb, scale, gap); - } + trackOffset = 0; + thumbOffset = (trackHeight - thumbHeight) / 2; + } + + if (track != null) { + track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft, + h - mPaddingBottom - trackOffset - mPaddingTop); + } + + if (thumb != null) { + setThumbPos(w, thumb, getScale(), thumbOffset); } } + private float getScale() { + final int max = getMax(); + return max > 0 ? getProgress() / (float) max : 0; + } + /** - * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and + * Updates the thumb drawable bounds. + * + * @param w Width of the view, including padding + * @param thumb Drawable used for the thumb + * @param scale Current progress between 0 and 1 + * @param offset Vertical offset for centering. If set to + * {@link Integer#MIN_VALUE}, the current offset will be used. */ - private void setThumbPos(int w, Drawable thumb, float scale, int gap) { + private void setThumbPos(int w, Drawable thumb, float scale, int offset) { int available = w - mPaddingLeft - mPaddingRight; final int thumbWidth = thumb.getIntrinsicWidth(); final int thumbHeight = thumb.getIntrinsicHeight(); @@ -301,13 +335,13 @@ public abstract class AbsSeekBar extends ProgressBar { final int thumbPos = (int) (scale * available + 0.5f); final int top, bottom; - if (gap == Integer.MIN_VALUE) { + if (offset == Integer.MIN_VALUE) { final Rect oldBounds = thumb.getBounds(); top = oldBounds.top; bottom = oldBounds.bottom; } else { - top = gap; - bottom = gap + thumbHeight; + top = offset; + bottom = offset + thumbHeight; } final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos; @@ -342,6 +376,33 @@ public abstract class AbsSeekBar extends ProgressBar { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); + drawThumb(canvas); + } + + @Override + void drawTrack(Canvas canvas) { + final Drawable thumbDrawable = mThumb; + if (thumbDrawable != null && mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + final Rect tempRect = mTempRect; + thumbDrawable.copyBounds(tempRect); + tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + super.drawTrack(canvas); + canvas.restoreToCount(saveCount); + } else { + super.drawTrack(canvas); + } + } + + /** + * Draw the thumb. + */ + void drawThumb(Canvas canvas) { if (mThumb != null) { canvas.save(); // Translate the padding. For the x, we need to allow the thumb to @@ -366,17 +427,17 @@ public abstract class AbsSeekBar extends ProgressBar { } dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; - + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), resolveSizeAndState(dh, heightMeasureSpec, 0)); } - + @Override public boolean onTouchEvent(MotionEvent event) { if (!mIsUserSeekable || !isEnabled()) { return false; } - + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (isInScrollingContainer()) { @@ -391,7 +452,7 @@ public abstract class AbsSeekBar extends ProgressBar { attemptClaimDrag(); } break; - + case MotionEvent.ACTION_MOVE: if (mIsDragging) { trackTouchEvent(event); @@ -408,7 +469,7 @@ public abstract class AbsSeekBar extends ProgressBar { } } break; - + case MotionEvent.ACTION_UP: if (mIsDragging) { trackTouchEvent(event); @@ -426,7 +487,7 @@ public abstract class AbsSeekBar extends ProgressBar { // value has not apparently changed) invalidate(); break; - + case MotionEvent.ACTION_CANCEL: if (mIsDragging) { onStopTrackingTouch(); @@ -493,7 +554,7 @@ public abstract class AbsSeekBar extends ProgressBar { mParent.requestDisallowInterceptTouchEvent(true); } } - + /** * This is called when the user has started touching this widget. */ @@ -526,7 +587,7 @@ public abstract class AbsSeekBar extends ProgressBar { setProgress(progress - mKeyProgressIncrement, true); onKeyChange(); return true; - + case KeyEvent.KEYCODE_DPAD_RIGHT: if (progress >= getMax()) break; setProgress(progress + mKeyProgressIncrement, true); @@ -595,17 +656,13 @@ public abstract class AbsSeekBar extends ProgressBar { public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - - Drawable thumb = mThumb; + final Drawable thumb = mThumb; if (thumb != null) { - setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE); + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index fa37443..83fbe8f 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -16,7 +16,14 @@ package android.widget; +import android.content.res.TypedArray; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Xfermode; +import android.util.Log; import com.android.internal.R; import android.content.Context; @@ -59,12 +66,10 @@ public class EdgeEffect { private static final int PULL_DECAY_TIME = 1000; private static final float MAX_ALPHA = 1.f; - private static final float HELD_EDGE_SCALE_Y = 0.5f; - private static final float MAX_GLOW_HEIGHT = 4.f; + private static final float MAX_GLOW_HEIGHT = 1.5f; - private static final float PULL_GLOW_BEGIN = 1.f; - private static final float PULL_EDGE_BEGIN = 0.6f; + private static final float PULL_GLOW_BEGIN = 0.f; // Minimum velocity that will be absorbed private static final int MIN_VELOCITY = 100; @@ -73,24 +78,11 @@ public class EdgeEffect { private static final float EPSILON = 0.001f; - private final Drawable mEdge; - private final Drawable mGlow; - private int mWidth; - private int mHeight; - private int mX; - private int mY; - private static final int MIN_WIDTH = 300; - private final int mMinWidth; - - private float mEdgeAlpha; - private float mEdgeScaleY; + private static final float SIN_45 = (float) Math.sin(Math.PI / 4); + private float mGlowAlpha; private float mGlowScaleY; - private float mEdgeAlphaStart; - private float mEdgeAlphaFinish; - private float mEdgeScaleYStart; - private float mEdgeScaleYFinish; private float mGlowAlphaStart; private float mGlowAlphaFinish; private float mGlowScaleYStart; @@ -107,16 +99,11 @@ public class EdgeEffect { private static final int STATE_RECEDE = 3; private static final int STATE_PULL_DECAY = 4; - // How much dragging should effect the height of the edge image. - // Number determined by user testing. - private static final int PULL_DISTANCE_EDGE_FACTOR = 7; - // How much dragging should effect the height of the glow image. // Number determined by user testing. private static final int PULL_DISTANCE_GLOW_FACTOR = 7; private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f; - private static final int VELOCITY_EDGE_FACTOR = 8; private static final int VELOCITY_GLOW_FACTOR = 12; private int mState = STATE_IDLE; @@ -124,30 +111,26 @@ public class EdgeEffect { private float mPullDistance; private final Rect mBounds = new Rect(); - - private final int mEdgeHeight; - private final int mGlowHeight; - private final int mGlowWidth; - private final int mMaxEffectHeight; + private final RectF mArcRect = new RectF(); + private final Paint mPaint = new Paint(); + private float mRadius; + private float mDisplacement = 0.5f; + private float mTargetDisplacement = 0.5f; /** * Construct a new EdgeEffect with a theme appropriate for the provided context. * @param context Context used to provide theming and resource information for the EdgeEffect */ public EdgeEffect(Context context) { - final Resources res = context.getResources(); - mEdge = context.getDrawable(R.drawable.overscroll_edge); - mGlow = context.getDrawable(R.drawable.overscroll_glow); - - mEdgeHeight = mEdge.getIntrinsicHeight(); - mGlowHeight = mGlow.getIntrinsicHeight(); - mGlowWidth = mGlow.getIntrinsicWidth(); - - mMaxEffectHeight = (int) (Math.min( - mGlowHeight * MAX_GLOW_HEIGHT * mGlowHeight / mGlowWidth * 0.6f, - mGlowHeight * MAX_GLOW_HEIGHT) + 0.5f); - - mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f); + mPaint.setAntiAlias(true); + final TypedArray a = context.obtainStyledAttributes( + com.android.internal.R.styleable.EdgeEffect); + final int themeColor = a.getColor( + com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666); + a.recycle(); + mPaint.setColor((themeColor & 0xffffff) | 0x66000000); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mInterpolator = new DecelerateInterpolator(); } @@ -158,20 +141,12 @@ public class EdgeEffect { * @param height Effect height in pixels */ public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - } + final float r = width * 0.5f / SIN_45; + final float y = SIN_45 * r; + final float h = r - y; + mRadius = r; - /** - * Set the position of this edge effect in pixels. This position is - * only used by {@link #getBounds(boolean)}. - * - * @param x The position of the edge effect on the X axis - * @param y The position of the edge effect on the Y axis - */ - void setPosition(int x, int y) { - mX = x; - mY = y; + mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h)); } /** @@ -199,17 +174,38 @@ public class EdgeEffect { * The host view should always {@link android.view.View#invalidate()} after this * and draw the results accordingly. * + * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement + * of the pull point is known.</p> + * * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to * 1.f (full length of the view) or negative values to express change * back toward the edge reached to initiate the effect. */ public void onPull(float deltaDistance) { + onPull(deltaDistance, 0.5f); + } + + /** + * A view should call this when content is pulled away from an edge by the user. + * This will update the state of the current visual effect and its associated animation. + * The host view should always {@link android.view.View#invalidate()} after this + * and draw the results accordingly. + * + * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to + * 1.f (full length of the view) or negative values to express change + * back toward the edge reached to initiate the effect. + * @param displacement The displacement from the starting side of the effect of the point + * initiating the pull. In the case of touch this is the finger position. + * Values may be from 0-1. + */ + public void onPull(float deltaDistance, float displacement) { final long now = AnimationUtils.currentAnimationTimeMillis(); + mTargetDisplacement = displacement; if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) { return; } if (mState != STATE_PULL) { - mGlowScaleY = PULL_GLOW_BEGIN; + mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY); } mState = STATE_PULL; @@ -217,15 +213,10 @@ public class EdgeEffect { mDuration = PULL_TIME; mPullDistance += deltaDistance; - float distance = Math.abs(mPullDistance); - - mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA)); - mEdgeScaleY = mEdgeScaleYStart = Math.max( - HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f)); mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, mGlowAlpha + - (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); + (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); float glowChange = Math.abs(deltaDistance); if (deltaDistance > 0 && mPullDistance < 0) { @@ -239,8 +230,6 @@ public class EdgeEffect { mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max( 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR)); - mEdgeAlphaFinish = mEdgeAlpha; - mEdgeScaleYFinish = mEdgeScaleY; mGlowAlphaFinish = mGlowAlpha; mGlowScaleYFinish = mGlowScaleY; } @@ -259,13 +248,9 @@ public class EdgeEffect { } mState = STATE_RECEDE; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; @@ -290,30 +275,21 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = 0.15f + (velocity * 0.02f); - // The edge should always be at least partially visible, regardless - // of velocity. - mEdgeAlphaStart = 0.f; - mEdgeScaleY = mEdgeScaleYStart = 0.f; // The glow depends more on the velocity, and therefore starts out // nearly invisible. mGlowAlphaStart = 0.3f; - mGlowScaleYStart = 0.f; + mGlowScaleYStart = Math.max(mGlowScaleY, 0.f); - // Factor the velocity by 8. Testing on device shows this works best to - // reflect the strength of the user's scrolling. - mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1)); - // Edge should never get larger than the size of its asset. - mEdgeScaleYFinish = Math.max( - HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f)); // Growth for the size of the glow should be quadratic to properly // respond // to a user's scrolling speed. The faster the scrolling speed, the more // intense the effect should be for both the size and the saturation. - mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f); + mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f); // Alpha should change for the glow as well as size. mGlowAlphaFinish = Math.max( mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA)); + mTargetDisplacement = 0.5f; } @@ -330,52 +306,42 @@ public class EdgeEffect { public boolean draw(Canvas canvas) { update(); - mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255)); - - int glowBottom = (int) Math.min( - mGlowHeight * mGlowScaleY * mGlowHeight / mGlowWidth * 0.6f, - mGlowHeight * MAX_GLOW_HEIGHT); - if (mWidth < mMinWidth) { - // Center the glow and clip it. - int glowLeft = (mWidth - mMinWidth)/2; - mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom); - } else { - // Stretch the glow to fit. - mGlow.setBounds(0, 0, mWidth, glowBottom); - } + final int count = canvas.save(); - mGlow.draw(canvas); + final float y = mBounds.height(); + final float centerY = y - mRadius; + final float centerX = mBounds.centerX(); + mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius); + canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0); - mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255)); - - int edgeBottom = (int) (mEdgeHeight * mEdgeScaleY); - if (mWidth < mMinWidth) { - // Center the edge and clip it. - int edgeLeft = (mWidth - mMinWidth)/2; - mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom); - } else { - // Stretch the edge to fit. - mEdge.setBounds(0, 0, mWidth, edgeBottom); + final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f; + float translateX = mBounds.width() * displacement; + float translateY = 0; + if (mGlowScaleY > 1.f) { + translateY = (mGlowScaleY - 1.f) * mBounds.height(); } - mEdge.draw(canvas); - - if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) { + canvas.clipRect(Float.MIN_VALUE, mBounds.top, + Float.MAX_VALUE, Float.MAX_VALUE); + canvas.translate(translateX, translateY); + canvas.drawArc(mArcRect, 0, 180, true, mPaint); + canvas.restoreToCount(count); + + boolean oneLastFrame = false; + if (mState == STATE_RECEDE && mGlowScaleY == 0) { mState = STATE_IDLE; + oneLastFrame = true; } - return mState != STATE_IDLE; + return mState != STATE_IDLE || oneLastFrame; } /** - * Returns the bounds of the edge effect. - * - * @hide + * Return the maximum height that the edge effect will be drawn at given the original + * {@link #setSize(int, int) input size}. + * @return The maximum height of the edge effect */ - public Rect getBounds(boolean reverse) { - mBounds.set(0, 0, mWidth, mMaxEffectHeight); - mBounds.offset(mX, mY - (reverse ? mMaxEffectHeight : 0)); - - return mBounds; + public int getMaxHeight() { + return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f); } private void update() { @@ -384,10 +350,9 @@ public class EdgeEffect { final float interp = mInterpolator.getInterpolation(t); - mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp; - mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp; mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp; mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp; + mDisplacement = (mDisplacement + mTargetDisplacement) / 2; if (t >= 1.f - EPSILON) { switch (mState) { @@ -396,14 +361,10 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = RECEDE_TIME; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - // After absorb, the glow and edge should fade to nothing. - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; + // After absorb, the glow should fade to nothing. mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; break; @@ -412,26 +373,14 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = PULL_DECAY_TIME; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - // After pull, the glow and edge should fade to nothing. - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; + // After pull, the glow should fade to nothing. mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; break; case STATE_PULL_DECAY: - // When receding, we want edge to decrease more slowly - // than the glow. - float factor = mGlowScaleYFinish != 0 ? 1 - / (mGlowScaleYFinish * mGlowScaleYFinish) - : Float.MAX_VALUE; - mEdgeScaleY = mEdgeScaleYStart + - (mEdgeScaleYFinish - mEdgeScaleYStart) * - interp * factor; mState = STATE_RECEDE; break; case STATE_RECEDE: diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 25d4f42..0c65c50 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -616,12 +616,14 @@ public class HorizontalScrollView extends FrameLayout { if (canOverscroll) { final int pulledToX = oldX + deltaX; if (pulledToX < 0) { - mEdgeGlowLeft.onPull((float) deltaX / getWidth()); + mEdgeGlowLeft.onPull((float) deltaX / getWidth(), + 1.f - ev.getY(activePointerIndex) / getHeight()); if (!mEdgeGlowRight.isFinished()) { mEdgeGlowRight.onRelease(); } } else if (pulledToX > range) { - mEdgeGlowRight.onPull((float) deltaX / getWidth()); + mEdgeGlowRight.onPull((float) deltaX / getWidth(), + ev.getY(activePointerIndex) / getHeight()); if (!mEdgeGlowLeft.isFinished()) { mEdgeGlowLeft.onRelease(); } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index f7e81b8..0c3715d 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1066,21 +1066,30 @@ public class ProgressBar extends View { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - Drawable d = mCurrentDrawable; + drawTrack(canvas); + } + + /** + * Draws the progress bar track. + */ + void drawTrack(Canvas canvas) { + final Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation - canvas.save(); + final int saveCount = canvas.save(); + if(isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { canvas.translate(mPaddingLeft, mPaddingTop); } - long time = getDrawingTime(); + + final long time = getDrawingTime(); if (mHasAnimation) { mAnimation.getTransformation(time, mTransformation); - float scale = mTransformation.getAlpha(); + final float scale = mTransformation.getAlpha(); try { mInDrawing = true; d.setLevel((int) (scale * MAX_LEVEL)); @@ -1089,8 +1098,10 @@ public class ProgressBar extends View { } postInvalidateOnAnimation(); } + d.draw(canvas); - canvas.restore(); + canvas.restoreToCount(saveCount); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false; diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 0fa75a6..fd04890 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -669,12 +669,14 @@ public class ScrollView extends FrameLayout { } else if (canOverscroll) { final int pulledToY = oldY + deltaY; if (pulledToY < 0) { - mEdgeGlowTop.onPull((float) deltaY / getHeight()); + mEdgeGlowTop.onPull((float) deltaY / getHeight(), + ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (pulledToY > range) { - mEdgeGlowBottom.onPull((float) deltaY / getHeight()); + mEdgeGlowBottom.onPull((float) deltaY / getHeight(), + 1.f - ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 08af4de..438e164 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -22,9 +22,11 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; @@ -85,6 +87,7 @@ public class Switch extends CompoundButton { private int mThumbTextPadding; private int mSwitchMinWidth; private int mSwitchPadding; + private boolean mSplitTrack; private CharSequence mTextOn; private CharSequence mTextOff; @@ -174,13 +177,13 @@ public class Switch extends CompoundButton { super(context, attrs, defStyleAttr, defStyleRes); mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - Resources res = getResources(); + + final Resources res = getResources(); mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes); - mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb); mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track); mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn); @@ -191,15 +194,16 @@ public class Switch extends CompoundButton { com.android.internal.R.styleable.Switch_switchMinWidth, 0); mSwitchPadding = a.getDimensionPixelSize( com.android.internal.R.styleable.Switch_switchPadding, 0); + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false); - int appearance = a.getResourceId( + final int appearance = a.getResourceId( com.android.internal.R.styleable.Switch_switchTextAppearance, 0); if (appearance != 0) { setSwitchTextAppearance(context, appearance); } a.recycle(); - ViewConfiguration config = ViewConfiguration.get(context); + final ViewConfiguration config = ViewConfiguration.get(context); mTouchSlop = config.getScaledTouchSlop(); mMinFlingVelocity = config.getScaledMinimumFlingVelocity(); @@ -469,6 +473,29 @@ public class Switch extends CompoundButton { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Returns the text displayed when the button is in the checked state. * * @attr ref android.R.styleable#Switch_textOn @@ -518,13 +545,15 @@ public class Switch extends CompoundButton { mTrackDrawable.getPadding(mTempRect); - final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()); + final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()) + + mThumbTextPadding * 2; + mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth()); + final int switchWidth = Math.max(mSwitchMinWidth, - maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right); + 2 * mThumbWidth + mTempRect.left + mTempRect.right); final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(), mThumbDrawable.getIntrinsicHeight()); - mThumbWidth = maxTextWidth + mThumbTextPadding * 2; mSwitchWidth = switchWidth; mSwitchHeight = switchHeight; @@ -777,7 +806,7 @@ public class Switch extends CompoundButton { final Drawable trackDrawable = mTrackDrawable; final Drawable thumbDrawable = mThumbDrawable; - // Draw the switch + // Layout the track. final int switchLeft = mSwitchLeft; final int switchTop = mSwitchTop; final int switchRight = mSwitchRight; @@ -793,9 +822,10 @@ public class Switch extends CompoundButton { // Relies on mTempRect, MUST be called first! final int thumbPos = getThumbOffset(); + // Layout the thumb. thumbDrawable.getPadding(tempRect); - int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; - int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; + final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; + final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom); final Drawable background = getBackground(); @@ -805,20 +835,32 @@ public class Switch extends CompoundButton { super.onDraw(canvas); - trackDrawable.draw(canvas); + if (mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + thumbDrawable.copyBounds(tempRect); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + trackDrawable.draw(canvas); + canvas.restoreToCount(saveCount); + } else { + trackDrawable.draw(canvas); + } final int saveCount = canvas.save(); canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom); thumbDrawable.draw(canvas); - final int drawableState[] = getDrawableState(); - if (mTextColors != null) { - mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); - } - mTextPaint.drawableState = drawableState; - final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout; if (switchText != null) { + final int drawableState[] = getDrawableState(); + if (mTextColors != null) { + mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); + } + mTextPaint.drawableState = drawableState; + final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2; final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2; canvas.translate(left, top); @@ -889,12 +931,16 @@ public class Switch extends CompoundButton { protected void drawableStateChanged() { super.drawableStateChanged(); - int[] myDrawableState = getDrawableState(); + final int[] myDrawableState = getDrawableState(); + + if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) { + // Handle changes to thumb width and height. + requestLayout(); + } - // Set the state of the Drawable - // Drawable may be null when checked state is set from XML, from super constructor - if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState); - if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState); + if (mTrackDrawable != null) { + mTrackDrawable.setState(myDrawableState); + } invalidate(); } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 4726da7..b568121 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,6 +26,7 @@ import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; @@ -240,6 +242,7 @@ public class AlertController { mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setContentView(mAlertDialogLayout); setupView(); + setupDecor(); } public void setTitle(CharSequence title) { @@ -415,7 +418,28 @@ public class AlertController { public boolean onKeyUp(int keyCode, KeyEvent event) { return mScrollView != null && mScrollView.executeKeyEvent(event); } - + + private void setupDecor() { + final View decor = mWindow.getDecorView(); + final View parent = mWindow.findViewById(R.id.parentPanel); + if (parent != null && decor != null) { + decor.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { + if (insets.isRound()) { + // TODO: Get the padding as a function of the window size. + int roundOffset = mContext.getResources().getDimensionPixelOffset( + R.dimen.alert_dialog_round_padding); + parent.setPadding(roundOffset, roundOffset, roundOffset, roundOffset); + } + return insets.consumeSystemWindowInsets(); + } + }); + decor.setFitsSystemWindows(true); + decor.requestApplyInsets(); + } + } + private void setupView() { LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); @@ -636,14 +660,31 @@ public class AlertController { private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel, View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) { - final int topBright = a.getResourceId( - R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright); - final int topDark = a.getResourceId( - R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark); - final int centerBright = a.getResourceId( - R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright); - final int centerDark = a.getResourceId( - R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark); + int fullDark = 0; + int topDark = 0; + int centerDark = 0; + int bottomDark = 0; + int fullBright = 0; + int topBright = 0; + int centerBright = 0; + int bottomBright = 0; + int bottomMedium = 0; + if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.KITKAT) { + fullDark = R.drawable.popup_full_dark; + topDark = R.drawable.popup_top_dark; + centerDark = R.drawable.popup_center_dark; + bottomDark = R.drawable.popup_bottom_dark; + fullBright = R.drawable.popup_full_bright; + topBright = R.drawable.popup_top_bright; + centerBright = R.drawable.popup_center_bright; + bottomBright = R.drawable.popup_bottom_bright; + bottomMedium = R.drawable.popup_bottom_medium; + } + topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright); + topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark); + centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright); + centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark); + /* We now set the background of all of the sections of the alert. * First collect together each section that is being displayed along @@ -707,22 +748,17 @@ public class AlertController { if (lastView != null) { if (setView) { - final int bottomBright = a.getResourceId( - R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright); - final int bottomMedium = a.getResourceId( - R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium); - final int bottomDark = a.getResourceId( - R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark); + bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright); + bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium); + bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark); // ListViews will use the Bright background, but buttons use the // Medium background. lastView.setBackgroundResource( lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark); } else { - final int fullBright = a.getResourceId( - R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright); - final int fullDark = a.getResourceId( - R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark); + fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright); + fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark); lastView.setBackgroundResource(lastLight ? fullBright : fullDark); } diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl index 1ea7409..7e7f0e1 100644 --- a/core/java/com/android/internal/app/IUsageStats.aidl +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -16,13 +16,17 @@ package com.android.internal.app; +import android.app.UsageStats; import android.content.ComponentName; -import com.android.internal.os.PkgUsageStats; +import android.content.res.Configuration; +import android.os.ParcelableParcel; interface IUsageStats { void noteResumeComponent(in ComponentName componentName); void notePauseComponent(in ComponentName componentName); void noteLaunchTime(in ComponentName componentName, int millis); - PkgUsageStats getPkgUsageStats(in ComponentName componentName); - PkgUsageStats[] getAllPkgUsageStats(); + void noteStartConfig(in Configuration config); + UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName); + UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg); + ParcelableParcel getCurrentStats(String callingPkg); } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 882bec9..41f3337 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1108,13 +1108,6 @@ public final class ProcessStats implements Parcelable { mRuntime = runtime; } } - String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview"; - if (!Objects.equals(webview, mWebView)) { - changed = true; - if (update) { - mWebView = webview; - } - } return changed; } diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java deleted file mode 100644 index f484724..0000000 --- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java +++ /dev/null @@ -1,188 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.Settings; -import android.text.SpannableString; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * This NotificationScorer bumps up the priority of notifications that contain references to the - * display names of starred contacts. The references it picks up are spannable strings which, in - * their entirety, match the display name of some starred contact. The magnitude of the bump ranges - * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and - * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification - * extra will be used to specify contact identifiers. - */ - -public class DemoContactNotificationScorer implements NotificationScorer { - private static final String TAG = "DemoContactNotificationScorer"; - private static final boolean DBG = false; - - protected static final boolean ENABLE_CONTACT_SCORER = true; - private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled"; - protected boolean mEnabled; - - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - private Context mContext; - - private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList( - Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES, - Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE - ); - - private static final String[] PROJECTION = new String[] { - ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME - }; - - private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI; - - private static List<String> extractSpannedStrings(CharSequence charSequence) { - if (charSequence == null) return Collections.emptyList(); - if (!(charSequence instanceof SpannableString)) { - return Arrays.asList(charSequence.toString()); - } - SpannableString spannableString = (SpannableString)charSequence; - // get all spans - Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class); - // spanned string sequences - ArrayList<String> sss = new ArrayList<String>(); - for (Object spanObj : ssArr) { - try { - sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj), - spannableString.getSpanEnd(spanObj)).toString()); - } catch(StringIndexOutOfBoundsException e) { - Slog.e(TAG, "Bad indices when extracting spanned subsequence", e); - } - } - return sss; - }; - - private static String getQuestionMarksInParens(int n) { - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < n; i++) { - if (sb.length() > 1) sb.append(','); - sb.append('?'); - } - sb.append(")"); - return sb.toString(); - } - - private boolean hasStarredContact(Bundle extras) { - if (extras == null) return false; - ArrayList<String> qStrings = new ArrayList<String>(); - // build list to query against the database for display names. - for (String rk: RELEVANT_KEYS_LIST) { - if (extras.get(rk) == null) { - continue; - } else if (extras.get(rk) instanceof CharSequence) { - qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk))); - } else if (extras.get(rk) instanceof CharSequence[]) { - // this is intended for Notification.EXTRA_TEXT_LINES - for (CharSequence line: (CharSequence[]) extras.get(rk)){ - qStrings.addAll(extractSpannedStrings(line)); - } - } else { - Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type."); - } - } - if (qStrings.isEmpty()) return false; - String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]); - - String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN " - + getQuestionMarksInParens(qStringsArr.length) + " AND " - + ContactsContract.Contacts.STARRED+" ='1'"; - - Cursor c = null; - try { - c = mContext.getContentResolver().query( - CONTACTS_URI, PROJECTION, selection, qStringsArr, null); - if (c != null) return c.getCount() > 0; - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - return false; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - boolean hasStarredPriority = hasStarredContact(notification.extras); - - if (DBG) { - if (hasStarredPriority) { - Slog.v(TAG, "Notification references starred contact. Promoted!"); - } else { - Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!"); - } - } - if (hasStarredPriority) score = priorityBumpMap(score); - return score; - } -} - diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java deleted file mode 100644 index 863c08c..0000000 --- a/core/java/com/android/internal/notification/NotificationScorer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; - -public interface NotificationScorer { - - public void initialize(Context context); - public int getScore(Notification notification, int score); - -} diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java deleted file mode 100644 index efb5f63..0000000 --- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java +++ /dev/null @@ -1,227 +0,0 @@ -/* -* 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 com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.ContactsContract.Contacts; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.LruCache; -import android.util.Slog; - -/** - * This {@link NotificationScorer} attempts to validate people references. - * Also elevates the priority of real people. - */ -public class PeopleNotificationScorer implements NotificationScorer { - private static final String TAG = "PeopleNotificationScorer"; - private static final boolean DBG = false; - - private static final boolean ENABLE_PEOPLE_SCORER = true; - private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled"; - private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; - private static final int MAX_PEOPLE = 10; - private static final int PEOPLE_CACHE_SIZE = 200; - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - protected boolean mEnabled; - private Context mContext; - - // maps raw person handle to resolved person object - private LruCache<String, LookupResult> mPeopleCache; - - private float findMaxContactScore(Bundle extras) { - if (extras == null) { - return 0f; - } - - final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); - if (people == null || people.length == 0) { - return 0f; - } - - float rank = 0f; - for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { - final String handle = people[personIdx]; - if (TextUtils.isEmpty(handle)) continue; - - LookupResult lookupResult = mPeopleCache.get(handle); - if (lookupResult == null || lookupResult.isExpired()) { - final Uri uri = Uri.parse(handle); - if ("tel".equals(uri.getScheme())) { - if (DBG) Slog.w(TAG, "checking telephone URI: " + handle); - lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart()); - } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { - if (DBG) Slog.w(TAG, "checking lookup URI: " + handle); - lookupResult = resolveContactsUri(handle, uri); - } else { - if (DBG) Slog.w(TAG, "unsupported URI " + handle); - } - } else { - if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId); - } - if (lookupResult != null) { - rank = Math.max(rank, lookupResult.getRank()); - } - } - return rank; - } - - private LookupResult lookupPhoneContact(final String handle, final String number) { - LookupResult lookupResult = null; - Cursor c = null; - try { - Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(number)); - c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private LookupResult resolveContactsUri(String handle, final Uri personUri) { - LookupResult lookupResult = null; - Cursor c = null; - try { - c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - // TODO: rework this function before shipping - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); - mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - float contactScore = findMaxContactScore(notification.extras); - if (contactScore > 0f) { - if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!"); - score = priorityBumpMap(score); - } else { - if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!"); - } - return score; - } - - private static class LookupResult { - private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr - public static final int INVALID_ID = -1; - - private final long mExpireMillis; - private int mId; - - public LookupResult(int id) { - mId = id; - mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; - } - - public boolean isExpired() { - return mExpireMillis < System.currentTimeMillis(); - } - - public boolean isInvalid() { - return mId == INVALID_ID || isExpired(); - } - - public float getRank() { - if (isInvalid()) { - return 0f; - } else { - return 1f; // TODO: finer grained score - } - } - - public LookupResult setId(int id) { - mId = id; - return this; - } - } -} - diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1aff190..7bd5b12 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -188,8 +188,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mShuttingDown; - HashMap<String, SparseBooleanArray>[] mActiveEvents - = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT]; + final HistoryEventTracker mActiveEvents = new HistoryEventTracker(); long mHistoryBaseTime; boolean mHaveBatteryLevel = false; @@ -2297,44 +2296,8 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteEventLocked(int code, String name, int uid) { uid = mapUid(uid); - if ((code&HistoryItem.EVENT_FLAG_START) != 0) { - int idx = code&~HistoryItem.EVENT_FLAG_START; - HashMap<String, SparseBooleanArray> active = mActiveEvents[idx]; - if (active == null) { - active = new HashMap<String, SparseBooleanArray>(); - mActiveEvents[idx] = active; - } - SparseBooleanArray uids = active.get(name); - if (uids == null) { - uids = new SparseBooleanArray(); - active.put(name, uids); - } - if (uids.get(uid)) { - // Already set, nothing to do! - return; - } - uids.put(uid, true); - } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) { - int idx = code&~HistoryItem.EVENT_FLAG_FINISH; - HashMap<String, SparseBooleanArray> active = mActiveEvents[idx]; - if (active == null) { - // not currently active, nothing to do. - return; - } - SparseBooleanArray uids = active.get(name); - if (uids == null) { - // not currently active, nothing to do. - return; - } - idx = uids.indexOfKey(uid); - if (idx < 0 || !uids.valueAt(idx)) { - // not currently active, nothing to do. - return; - } - uids.removeAt(idx); - if (uids.size() <= 0) { - active.remove(name); - } + if (!mActiveEvents.updateState(code, name, uid, 0)) { + return; } final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -2348,6 +2311,9 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private String mInitialAcquireWakeName; + private int mInitialAcquireWakeUid = -1; + public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging, long elapsedRealtime, long uptime) { uid = mapUid(uid); @@ -2360,8 +2326,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: " + Integer.toHexString(mHistoryCur.states)); mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : name; - mHistoryCur.wakelockTag.uid = uid; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName + = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; mWakeLockImportant = !unimportantForLogging; addHistoryRecordLocked(elapsedRealtime, uptime); } else if (!mWakeLockImportant && !unimportantForLogging) { @@ -2369,8 +2336,9 @@ public final class BatteryStatsImpl extends BatteryStats { // We'll try to update the last tag. mHistoryLastWritten.wakelockTag = null; mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : name; - mHistoryCur.wakelockTag.uid = uid; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName + = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; addHistoryRecordLocked(elapsedRealtime, uptime); } mWakeLockImportant = true; @@ -2395,6 +2363,14 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: " + Integer.toHexString(mHistoryCur.states)); + if ((name != null && !name.equals(mInitialAcquireWakeName)) + || uid != mInitialAcquireWakeUid) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = name; + mHistoryCur.wakelockTag.uid = uid; + } + mInitialAcquireWakeName = null; + mInitialAcquireWakeUid = -1; addHistoryRecordLocked(elapsedRealtime, uptime); } } @@ -5699,7 +5675,8 @@ public final class BatteryStatsImpl extends BatteryStats { final long lastRealtime = out.time; final long lastWalltime = out.currentTime; readHistoryDelta(mHistoryBuffer, out); - if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) { + if (out.cmd != HistoryItem.CMD_CURRENT_TIME + && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) { out.currentTime = lastWalltime + (out.time - lastRealtime); } return true; @@ -5845,17 +5822,15 @@ public final class BatteryStatsImpl extends BatteryStats { private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) { for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { - HashMap<String, SparseBooleanArray> active = mActiveEvents[i]; + HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i); if (active == null) { continue; } - for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) { - SparseBooleanArray uids = ent.getValue(); + for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) { + SparseIntArray uids = ent.getValue(); for (int j=0; j<uids.size(); j++) { - if (uids.valueAt(j)) { - addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(), - uids.keyAt(j)); - } + addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(), + uids.keyAt(j)); } } } @@ -5971,7 +5946,8 @@ public final class BatteryStatsImpl extends BatteryStats { boolean reset) { mRecordingHistory = true; mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, + reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; if (reset) { diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java deleted file mode 100644 index 8c2c405..0000000 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.os; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.HashMap; -import java.util.Map; - -/** - * implementation of PkgUsageStats associated with an - * application package. - * @hide - */ -public class PkgUsageStats implements Parcelable { - public String packageName; - public int launchCount; - public long usageTime; - public Map<String, Long> componentResumeTimes; - - public static final Parcelable.Creator<PkgUsageStats> CREATOR - = new Parcelable.Creator<PkgUsageStats>() { - public PkgUsageStats createFromParcel(Parcel in) { - return new PkgUsageStats(in); - } - - public PkgUsageStats[] newArray(int size) { - return new PkgUsageStats[size]; - } - }; - - public String toString() { - return "PkgUsageStats{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + packageName + "}"; - } - - public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { - packageName = pkgName; - launchCount = count; - usageTime = time; - componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); - } - - public PkgUsageStats(Parcel source) { - packageName = source.readString(); - launchCount = source.readInt(); - usageTime = source.readLong(); - final int N = source.readInt(); - componentResumeTimes = new HashMap<String, Long>(N); - for (int i = 0; i < N; i++) { - String component = source.readString(); - long lastResumeTime = source.readLong(); - componentResumeTimes.put(component, lastResumeTime); - } - } - - public PkgUsageStats(PkgUsageStats pStats) { - packageName = pStats.packageName; - launchCount = pStats.launchCount; - usageTime = pStats.usageTime; - componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(packageName); - dest.writeInt(launchCount); - dest.writeLong(usageTime); - dest.writeInt(componentResumeTimes.size()); - for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { - dest.writeString(ent.getKey()); - dest.writeLong(ent.getValue()); - } - } -} diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 52281d9..34f62ba 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -450,6 +450,7 @@ public class AsyncChannel { public void disconnect() { if ((mConnection != null) && (mSrcContext != null)) { mSrcContext.unbindService(mConnection); + mConnection = null; } try { // Send the DISCONNECTED, although it may not be received @@ -463,10 +464,12 @@ public class AsyncChannel { // Tell source we're disconnected. if (mSrcHandler != null) { replyDisconnected(STATUS_SUCCESSFUL); + mSrcHandler = null; } // Unlink only when bindService isn't used if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) { mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0); + mDeathMonitor = null; } } diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java new file mode 100644 index 0000000..0bd4d3a --- /dev/null +++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java @@ -0,0 +1,47 @@ +/* + * 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 com.android.internal.util; + +/** + * Helper class that contains a strong reference to a VirtualRefBase native + * object. This will incStrong in the ctor, and decStrong in the finalizer + */ +public final class VirtualRefBasePtr { + private long mNativePtr; + + public VirtualRefBasePtr(long ptr) { + mNativePtr = ptr; + nIncStrong(mNativePtr); + } + + public long get() { + return mNativePtr; + } + + @Override + protected void finalize() throws Throwable { + try { + nDecStrong(mNativePtr); + mNativePtr = 0; + } finally { + super.finalize(); + } + } + + private static native void nIncStrong(long ptr); + private static native void nDecStrong(long ptr); +} diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 90210ce..367b713 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -21,6 +21,7 @@ import android.os.Bundle; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; /** @@ -47,4 +48,6 @@ oneway interface IInputMethodSession { void toggleSoftInput(int showFlags, int hideFlags); void finishSession(); + + void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo); } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 51e2871..26f77b5 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -155,7 +155,8 @@ LOCAL_SRC_FILES:= \ android_content_res_Configuration.cpp \ android_animation_PropertyValuesHolder.cpp \ com_android_internal_net_NetworkStatsFactory.cpp \ - com_android_internal_os_Zygote.cpp + com_android_internal_os_Zygote.cpp \ + com_android_internal_util_VirtualRefBasePtr.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 66fbb8e..9941cd9 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -181,6 +181,7 @@ extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); +extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1262,6 +1263,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_MemoryFile), REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_com_android_internal_os_Zygote), + REG_JNI(register_com_android_internal_util_VirtualRefBasePtr), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_camera2_CameraMetadata), REG_JNI(register_android_hardware_SensorManager), diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index e0fa9ba..0328517 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1,838 +1,838 @@ -#include "SkBitmap.h"
-#include "SkPixelRef.h"
-#include "SkImageEncoder.h"
-#include "SkColorPriv.h"
-#include "GraphicsJNI.h"
-#include "SkDither.h"
-#include "SkUnPreMultiply.h"
-#include "SkStream.h"
-
-#include <binder/Parcel.h>
-#include "android_os_Parcel.h"
-#include "android_util_Binder.h"
-#include "android_nio_utils.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-
-#include <jni.h>
-
-#include <Caches.h>
-
-#if 0
- #define TRACE_BITMAP(code) code
-#else
- #define TRACE_BITMAP(code)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Conversions to/from SkColor, for get/setPixels, and the create method, which
-// is basically like setPixels
-
-typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
- int x, int y);
-
-static void FromColor_D32(void* dst, const SkColor src[], int width,
- int, int) {
- SkPMColor* d = (SkPMColor*)dst;
-
- for (int i = 0; i < width; i++) {
- *d++ = SkPreMultiplyColor(*src++);
- }
-}
-
-static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
- int, int) {
- // SkColor's ordering may be different from SkPMColor
- if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
- memcpy(dst, src, width * sizeof(SkColor));
- return;
- }
-
- // order isn't same, repack each pixel manually
- SkPMColor* d = (SkPMColor*)dst;
- for (int i = 0; i < width; i++) {
- SkColor c = *src++;
- *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
- SkColorGetG(c), SkColorGetB(c));
- }
-}
-
-static void FromColor_D565(void* dst, const SkColor src[], int width,
- int x, int y) {
- uint16_t* d = (uint16_t*)dst;
-
- DITHER_565_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkColor c = *src++;
- *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
- DITHER_VALUE(x));
- }
-}
-
-static void FromColor_D4444(void* dst, const SkColor src[], int width,
- int x, int y) {
- SkPMColor16* d = (SkPMColor16*)dst;
-
- DITHER_4444_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkPMColor pmc = SkPreMultiplyColor(*src++);
- *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
-// *d++ = SkPixel32ToPixel4444(pmc);
- }
-}
-
-static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
- int x, int y) {
- SkPMColor16* d = (SkPMColor16*)dst;
-
- DITHER_4444_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkColor c = *src++;
-
- // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
- SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
- SkColorGetG(c), SkColorGetB(c));
- *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
-// *d++ = SkPixel32ToPixel4444(pmc);
- }
-}
-
-// can return NULL
-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
- switch (config) {
- case SkBitmap::kARGB_8888_Config:
- return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
- case SkBitmap::kARGB_4444_Config:
- return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
- case SkBitmap::kRGB_565_Config:
- return FromColor_D565;
- default:
- break;
- }
- return NULL;
-}
-
-bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
- int x, int y, int width, int height,
- const SkBitmap& dstBitmap, bool isPremultiplied) {
- SkAutoLockPixels alp(dstBitmap);
- void* dst = dstBitmap.getPixels();
- FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
-
- if (NULL == dst || NULL == proc) {
- return false;
- }
-
- const jint* array = env->GetIntArrayElements(srcColors, NULL);
- const SkColor* src = (const SkColor*)array + srcOffset;
-
- // reset to to actual choice from caller
- dst = dstBitmap.getAddr(x, y);
- // now copy/convert each scanline
- for (int y = 0; y < height; y++) {
- proc(dst, src, width, x, y);
- src += srcStride;
- dst = (char*)dst + dstBitmap.rowBytes();
- }
-
- dstBitmap.notifyPixelsChanged();
-
- env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
- JNI_ABORT);
- return true;
-}
-
-//////////////////// ToColor procs
-
-typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
- SkColorTable*);
-
-static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
- } while (--width != 0);
-}
-
-static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- SkPMColor c = *s++;
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- SkPMColor c = *s++;
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- SkPMColor c = SkPixel4444ToPixel32(*s++);
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- SkPMColor c = SkPixel4444ToPixel32(*s++);
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S565(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const uint16_t* s = (const uint16_t*)src;
- do {
- uint16_t c = *s++;
- *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
- SkPacked16ToB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- SkPMColor c = colors[*s++];
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- SkPMColor c = colors[*s++];
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-// can return NULL
-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
- switch (src.config()) {
- case SkBitmap::kARGB_8888_Config:
- if (src.isOpaque()) return ToColor_S32_Opaque;
- return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
- case SkBitmap::kARGB_4444_Config:
- if (src.isOpaque()) return ToColor_S4444_Opaque;
- return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
- case SkBitmap::kRGB_565_Config:
- return ToColor_S565;
- case SkBitmap::kIndex8_Config:
- if (src.getColorTable() == NULL) {
- return NULL;
- }
- if (src.isOpaque()) return ToColor_SI8_Opaque;
- return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
- default:
- break;
- }
- return NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-static int getPremulBitmapCreateFlags(bool isMutable) {
- int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
- if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
- return flags;
-}
-
-static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
- jint offset, jint stride, jint width, jint height,
- jint configHandle, jboolean isMutable) {
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- if (NULL != jColors) {
- size_t n = env->GetArrayLength(jColors);
- if (n < SkAbs32(stride) * (size_t)height) {
- doThrowAIOOBE(env);
- return NULL;
- }
- }
-
- // ARGB_4444 is a deprecated format, convert automatically to 8888
- if (config == SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
- }
-
- SkBitmap bitmap;
- bitmap.setConfig(config, width, height);
-
- jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
- if (NULL == buff) {
- return NULL;
- }
-
- if (jColors != NULL) {
- GraphicsJNI::SetPixels(env, jColors, offset, stride,
- 0, 0, width, height, bitmap, true);
- }
-
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
- getPremulBitmapCreateFlags(isMutable), NULL, NULL);
-}
-
-static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
- jint dstConfigHandle, jboolean isMutable) {
- const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
- SkBitmap result;
- JavaPixelAllocator allocator(env);
-
+#include "SkBitmap.h" +#include "SkPixelRef.h" +#include "SkImageEncoder.h" +#include "SkColorPriv.h" +#include "GraphicsJNI.h" +#include "SkDither.h" +#include "SkUnPreMultiply.h" +#include "SkStream.h" + +#include <binder/Parcel.h> +#include "android_os_Parcel.h" +#include "android_util_Binder.h" +#include "android_nio_utils.h" +#include "CreateJavaOutputStreamAdaptor.h" + +#include <jni.h> + +#include <Caches.h> + +#if 0 + #define TRACE_BITMAP(code) code +#else + #define TRACE_BITMAP(code) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Conversions to/from SkColor, for get/setPixels, and the create method, which +// is basically like setPixels + +typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, + int x, int y); + +static void FromColor_D32(void* dst, const SkColor src[], int width, + int, int) { + SkPMColor* d = (SkPMColor*)dst; + + for (int i = 0; i < width; i++) { + *d++ = SkPreMultiplyColor(*src++); + } +} + +static void FromColor_D32_Raw(void* dst, const SkColor src[], int width, + int, int) { + // SkColor's ordering may be different from SkPMColor + if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) { + memcpy(dst, src, width * sizeof(SkColor)); + return; + } + + // order isn't same, repack each pixel manually + SkPMColor* d = (SkPMColor*)dst; + for (int i = 0; i < width; i++) { + SkColor c = *src++; + *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), + SkColorGetG(c), SkColorGetB(c)); + } +} + +static void FromColor_D565(void* dst, const SkColor src[], int width, + int x, int y) { + uint16_t* d = (uint16_t*)dst; + + DITHER_565_SCAN(y); + for (int stop = x + width; x < stop; x++) { + SkColor c = *src++; + *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), + DITHER_VALUE(x)); + } +} + +static void FromColor_D4444(void* dst, const SkColor src[], int width, + int x, int y) { + SkPMColor16* d = (SkPMColor16*)dst; + + DITHER_4444_SCAN(y); + for (int stop = x + width; x < stop; x++) { + SkPMColor pmc = SkPreMultiplyColor(*src++); + *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); +// *d++ = SkPixel32ToPixel4444(pmc); + } +} + +static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width, + int x, int y) { + SkPMColor16* d = (SkPMColor16*)dst; + + DITHER_4444_SCAN(y); + for (int stop = x + width; x < stop; x++) { + SkColor c = *src++; + + // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied + SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), + SkColorGetG(c), SkColorGetB(c)); + *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); +// *d++ = SkPixel32ToPixel4444(pmc); + } +} + +// can return NULL +static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) { + switch (config) { + case SkBitmap::kARGB_8888_Config: + return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw; + case SkBitmap::kARGB_4444_Config: + return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw; + case SkBitmap::kRGB_565_Config: + return FromColor_D565; + default: + break; + } + return NULL; +} + +bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride, + int x, int y, int width, int height, + const SkBitmap& dstBitmap, bool isPremultiplied) { + SkAutoLockPixels alp(dstBitmap); + void* dst = dstBitmap.getPixels(); + FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied); + + if (NULL == dst || NULL == proc) { + return false; + } + + const jint* array = env->GetIntArrayElements(srcColors, NULL); + const SkColor* src = (const SkColor*)array + srcOffset; + + // reset to to actual choice from caller + dst = dstBitmap.getAddr(x, y); + // now copy/convert each scanline + for (int y = 0; y < height; y++) { + proc(dst, src, width, x, y); + src += srcStride; + dst = (char*)dst + dstBitmap.rowBytes(); + } + + dstBitmap.notifyPixelsChanged(); + + env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), + JNI_ABORT); + return true; +} + +//////////////////// ToColor procs + +typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, + SkColorTable*); + +static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor* s = (const SkPMColor*)src; + do { + *dst++ = SkUnPreMultiply::PMColorToColor(*s++); + } while (--width != 0); +} + +static void ToColor_S32_Raw(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor* s = (const SkPMColor*)src; + do { + SkPMColor c = *s++; + *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), + SkGetPackedG32(c), SkGetPackedB32(c)); + } while (--width != 0); +} + +static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor* s = (const SkPMColor*)src; + do { + SkPMColor c = *s++; + *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), + SkGetPackedB32(c)); + } while (--width != 0); +} + +static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor16* s = (const SkPMColor16*)src; + do { + *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++)); + } while (--width != 0); +} + +static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor16* s = (const SkPMColor16*)src; + do { + SkPMColor c = SkPixel4444ToPixel32(*s++); + *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), + SkGetPackedG32(c), SkGetPackedB32(c)); + } while (--width != 0); +} + +static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const SkPMColor16* s = (const SkPMColor16*)src; + do { + SkPMColor c = SkPixel4444ToPixel32(*s++); + *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), + SkGetPackedB32(c)); + } while (--width != 0); +} + +static void ToColor_S565(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + const uint16_t* s = (const uint16_t*)src; + do { + uint16_t c = *s++; + *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c), + SkPacked16ToB32(c)); + } while (--width != 0); +} + +static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, + SkColorTable* ctable) { + SkASSERT(width > 0); + const uint8_t* s = (const uint8_t*)src; + const SkPMColor* colors = ctable->lockColors(); + do { + *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]); + } while (--width != 0); + ctable->unlockColors(); +} + +static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width, + SkColorTable* ctable) { + SkASSERT(width > 0); + const uint8_t* s = (const uint8_t*)src; + const SkPMColor* colors = ctable->lockColors(); + do { + SkPMColor c = colors[*s++]; + *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), + SkGetPackedG32(c), SkGetPackedB32(c)); + } while (--width != 0); + ctable->unlockColors(); +} + +static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, + SkColorTable* ctable) { + SkASSERT(width > 0); + const uint8_t* s = (const uint8_t*)src; + const SkPMColor* colors = ctable->lockColors(); + do { + SkPMColor c = colors[*s++]; + *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), + SkGetPackedB32(c)); + } while (--width != 0); + ctable->unlockColors(); +} + +// can return NULL +static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) { + switch (src.config()) { + case SkBitmap::kARGB_8888_Config: + if (src.isOpaque()) return ToColor_S32_Opaque; + return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw; + case SkBitmap::kARGB_4444_Config: + if (src.isOpaque()) return ToColor_S4444_Opaque; + return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw; + case SkBitmap::kRGB_565_Config: + return ToColor_S565; + case SkBitmap::kIndex8_Config: + if (src.getColorTable() == NULL) { + return NULL; + } + if (src.isOpaque()) return ToColor_SI8_Opaque; + return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha; + default: + break; + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int getPremulBitmapCreateFlags(bool isMutable) { + int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied; + if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable; + return flags; +} + +static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, + jint offset, jint stride, jint width, jint height, + jint configHandle, jboolean isMutable) { + SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); + if (NULL != jColors) { + size_t n = env->GetArrayLength(jColors); + if (n < SkAbs32(stride) * (size_t)height) { + doThrowAIOOBE(env); + return NULL; + } + } + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (config == SkBitmap::kARGB_4444_Config) { + config = SkBitmap::kARGB_8888_Config; + } + + SkBitmap bitmap; + bitmap.setConfig(config, width, height); + + jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); + if (NULL == buff) { + return NULL; + } + + if (jColors != NULL) { + GraphicsJNI::SetPixels(env, jColors, offset, stride, + 0, 0, width, height, bitmap, true); + } + + return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, + getPremulBitmapCreateFlags(isMutable), NULL, NULL); +} + +static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, + jint dstConfigHandle, jboolean isMutable) { + const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle); + SkBitmap result; + JavaPixelAllocator allocator(env); + if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) { - return NULL;
- }
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
- getPremulBitmapCreateFlags(isMutable), NULL, NULL);
-}
-
-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
- return;
- }
-#endif // USE_OPENGL_RENDERER
- delete bitmap;
-}
-
-static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- bool result;
- result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
- return result ? JNI_TRUE : JNI_FALSE;
- }
-#endif // USE_OPENGL_RENDERER
- bitmap->setPixels(NULL, NULL);
- return JNI_TRUE;
-}
-
-static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
- jint width, jint height, jint configHandle, jint allocSize) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
- // done in native as there's no way to get BytesPerPixel in Java
- doThrowIAE(env, "Bitmap not large enough to support new configuration");
- return;
- }
- SkPixelRef* ref = bitmap->pixelRef();
- SkSafeRef(ref);
- bitmap->setConfig(config, width, height);
- bitmap->setPixelRef(ref);
-
- // notifyPixelsChanged will increment the generation ID even though the actual pixel data
- // hasn't been touched. This signals the renderer that the bitmap (including width, height,
- // and config) has changed.
- ref->notifyPixelsChanged();
- SkSafeUnref(ref);
-}
-
-// These must match the int values in Bitmap.java
-enum JavaEncodeFormat {
- kJPEG_JavaEncodeFormat = 0,
- kPNG_JavaEncodeFormat = 1,
- kWEBP_JavaEncodeFormat = 2
-};
-
-static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
- jint format, jint quality,
- jobject jstream, jbyteArray jstorage) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkImageEncoder::Type fm;
-
- switch (format) {
- case kJPEG_JavaEncodeFormat:
- fm = SkImageEncoder::kJPEG_Type;
- break;
- case kPNG_JavaEncodeFormat:
- fm = SkImageEncoder::kPNG_Type;
- break;
- case kWEBP_JavaEncodeFormat:
- fm = SkImageEncoder::kWEBP_Type;
- break;
- default:
- return JNI_FALSE;
- }
-
- bool success = false;
- if (NULL != bitmap) {
- SkAutoLockPixels alp(*bitmap);
-
- if (NULL == bitmap->getPixels()) {
- return JNI_FALSE;
- }
-
- SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
- if (NULL == strm) {
- return JNI_FALSE;
- }
-
- SkImageEncoder* encoder = SkImageEncoder::Create(fm);
- if (NULL != encoder) {
- success = encoder->encodeStream(strm, *bitmap, quality);
- delete encoder;
- }
- delete strm;
- }
- return success ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->eraseColor(color);
-}
-
-static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->rowBytes());
-}
-
-static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->config());
-}
-
-static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->getGenerationID());
-}
-
-static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
- jboolean hasAlpha, jboolean isPremul) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- if (!hasAlpha) {
- bitmap->setAlphaType(kOpaque_SkAlphaType);
- } else if (isPremul) {
- bitmap->setAlphaType(kPremul_SkAlphaType);
- } else {
- bitmap->setAlphaType(kUnpremul_SkAlphaType);
- }
-}
-
-static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
- jboolean hasMipMap) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->setHasHardwareMipMap(hasMipMap);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
- if (parcel == NULL) {
- SkDebugf("-------- unparcel parcel is NULL\n");
- return NULL;
- }
-
- android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
- const bool isMutable = p->readInt32() != 0;
- const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
- const int width = p->readInt32();
- const int height = p->readInt32();
- const int rowBytes = p->readInt32();
- const int density = p->readInt32();
-
- if (SkBitmap::kARGB_8888_Config != config &&
- SkBitmap::kRGB_565_Config != config &&
- SkBitmap::kARGB_4444_Config != config &&
- SkBitmap::kIndex8_Config != config &&
- SkBitmap::kA8_Config != config) {
- SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
- return NULL;
- }
-
- SkBitmap* bitmap = new SkBitmap;
-
- bitmap->setConfig(config, width, height, rowBytes);
-
- SkColorTable* ctable = NULL;
- if (config == SkBitmap::kIndex8_Config) {
- int count = p->readInt32();
- if (count > 0) {
- size_t size = count * sizeof(SkPMColor);
- const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
- ctable = new SkColorTable(src, count);
- }
- }
-
- jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
- if (NULL == buffer) {
- SkSafeUnref(ctable);
- delete bitmap;
- return NULL;
- }
-
- SkSafeUnref(ctable);
-
- size_t size = bitmap->getSize();
-
- android::Parcel::ReadableBlob blob;
- android::status_t status = p->readBlob(size, &blob);
- if (status) {
- doThrowRE(env, "Could not read bitmap from parcel blob.");
- delete bitmap;
- return NULL;
- }
-
- bitmap->lockPixels();
- memcpy(bitmap->getPixels(), blob.data(), size);
- bitmap->unlockPixels();
-
- blob.release();
-
- return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
- NULL, NULL, density);
-}
-
-static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
- jlong bitmapHandle,
- jboolean isMutable, jint density,
- jobject parcel) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- if (parcel == NULL) {
- SkDebugf("------- writeToParcel null parcel\n");
- return JNI_FALSE;
- }
-
- android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
- p->writeInt32(isMutable);
- p->writeInt32(bitmap->config());
- p->writeInt32(bitmap->width());
- p->writeInt32(bitmap->height());
- p->writeInt32(bitmap->rowBytes());
- p->writeInt32(density);
-
- if (bitmap->config() == SkBitmap::kIndex8_Config) {
- SkColorTable* ctable = bitmap->getColorTable();
- if (ctable != NULL) {
- int count = ctable->count();
- p->writeInt32(count);
- memcpy(p->writeInplace(count * sizeof(SkPMColor)),
- ctable->lockColors(), count * sizeof(SkPMColor));
- ctable->unlockColors();
- } else {
- p->writeInt32(0); // indicate no ctable
- }
- }
-
- size_t size = bitmap->getSize();
-
- android::Parcel::WritableBlob blob;
- android::status_t status = p->writeBlob(size, &blob);
- if (status) {
- doThrowRE(env, "Could not write bitmap to parcel blob.");
- return JNI_FALSE;
- }
-
- bitmap->lockPixels();
- const void* pSrc = bitmap->getPixels();
- if (pSrc == NULL) {
- memset(blob.data(), 0, size);
- } else {
- memcpy(blob.data(), pSrc, size);
- }
- bitmap->unlockPixels();
-
- blob.release();
- return JNI_TRUE;
-}
-
-static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
- jlong srcHandle, jlong paintHandle,
- jintArray offsetXY) {
- const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkIPoint offset;
- SkBitmap* dst = new SkBitmap;
- JavaPixelAllocator allocator(env);
-
- src->extractAlpha(dst, paint, &allocator, &offset);
- // If Skia can't allocate pixels for destination bitmap, it resets
- // it, that is set its pixels buffer to NULL, and zero width and height.
- if (dst->getPixels() == NULL && src->getPixels() != NULL) {
- delete dst;
- doThrowOOME(env, "failed to allocate pixels for alpha");
- return NULL;
- }
- if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
- int* array = env->GetIntArrayElements(offsetXY, NULL);
- array[0] = offset.fX;
- array[1] = offset.fY;
- env->ReleaseIntArrayElements(offsetXY, array, 0);
- }
-
- return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
- getPremulBitmapCreateFlags(true), NULL, NULL);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
-
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
- if (NULL == proc) {
- return 0;
- }
- const void* src = bitmap->getAddr(x, y);
- if (NULL == src) {
- return 0;
- }
-
- SkColor dst[1];
- proc(dst, src, 1, bitmap->getColorTable());
- return static_cast<jint>(dst[0]);
-}
-
-static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
- jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
-
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
- if (NULL == proc) {
- return;
- }
- const void* src = bitmap->getAddr(x, y);
- if (NULL == src) {
- return;
- }
-
- SkColorTable* ctable = bitmap->getColorTable();
- jint* dst = env->GetIntArrayElements(pixelArray, NULL);
- SkColor* d = (SkColor*)dst + offset;
- while (--height >= 0) {
- proc(d, src, width, ctable);
- d += stride;
- src = (void*)((const char*)src + bitmap->rowBytes());
- }
- env->ReleaseIntArrayElements(pixelArray, dst, 0);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkColor color = static_cast<SkColor>(colorHandle);
- SkAutoLockPixels alp(*bitmap);
- if (NULL == bitmap->getPixels()) {
- return;
- }
-
- FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
- if (NULL == proc) {
- return;
- }
-
- proc(bitmap->getAddr(x, y), &color, 1, x, y);
- bitmap->notifyPixelsChanged();
-}
-
-static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
- jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
- x, y, width, height, *bitmap, isPremultiplied);
-}
-
-static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
- jlong bitmapHandle, jobject jbuffer) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
- const void* src = bitmap->getPixels();
-
- if (NULL != src) {
- android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
-
- // the java side has already checked that buffer is large enough
- memcpy(abp.pointer(), src, bitmap->getSize());
- }
-}
-
-static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
- jlong bitmapHandle, jobject jbuffer) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
- void* dst = bitmap->getPixels();
-
- if (NULL != dst) {
- android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
- // the java side has already checked that buffer is large enough
- memcpy(dst, abp.pointer(), bitmap->getSize());
- bitmap->notifyPixelsChanged();
- }
-}
-
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
- jlong bm1Handle) {
- const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
- const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
- if (bm0->width() != bm1->width() ||
- bm0->height() != bm1->height() ||
- bm0->config() != bm1->config()) {
- return JNI_FALSE;
- }
-
- SkAutoLockPixels alp0(*bm0);
- SkAutoLockPixels alp1(*bm1);
-
- // if we can't load the pixels, return false
- if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
- return JNI_FALSE;
- }
-
- if (bm0->config() == SkBitmap::kIndex8_Config) {
- SkColorTable* ct0 = bm0->getColorTable();
- SkColorTable* ct1 = bm1->getColorTable();
- if (NULL == ct0 || NULL == ct1) {
- return JNI_FALSE;
- }
- if (ct0->count() != ct1->count()) {
- return JNI_FALSE;
- }
-
- SkAutoLockColors alc0(ct0);
- SkAutoLockColors alc1(ct1);
- const size_t size = ct0->count() * sizeof(SkPMColor);
- if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
- return JNI_FALSE;
- }
- }
-
- // now compare each scanline. We can't do the entire buffer at once,
- // since we don't care about the pixel values that might extend beyond
- // the width (since the scanline might be larger than the logical width)
- const int h = bm0->height();
- const size_t size = bm0->width() * bm0->bytesPerPixel();
- for (int y = 0; y < h; y++) {
- if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
- return JNI_FALSE;
- }
- }
- return JNI_TRUE;
-}
-
-static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->lockPixels();
- bitmap->unlockPixels();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-static JNINativeMethod gBitmapMethods[] = {
- { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_creator },
- { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copy },
- { "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
- { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
- { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure },
- { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
- (void*)Bitmap_compress },
- { "nativeErase", "(JI)V", (void*)Bitmap_erase },
- { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
- { "nativeConfig", "(J)I", (void*)Bitmap_config },
- { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
- { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
- { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap },
- { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap },
- { "nativeCreateFromParcel",
- "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
- (void*)Bitmap_createFromParcel },
- { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z",
- (void*)Bitmap_writeToParcel },
- { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
- (void*)Bitmap_extractAlpha },
- { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId },
- { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel },
- { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
- { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel },
- { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
- { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsToBuffer },
- { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsFromBuffer },
- { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
- { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
-};
-
-#define kClassPathName "android/graphics/Bitmap"
-
-int register_android_graphics_Bitmap(JNIEnv* env)
-{
- return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
- gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
-}
+ return NULL; + } + return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), + getPremulBitmapCreateFlags(isMutable), NULL, NULL); +} + +static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); +#ifdef USE_OPENGL_RENDERER + if (android::uirenderer::Caches::hasInstance()) { + android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap); + return; + } +#endif // USE_OPENGL_RENDERER + delete bitmap; +} + +static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); +#ifdef USE_OPENGL_RENDERER + if (android::uirenderer::Caches::hasInstance()) { + bool result; + result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap); + return result ? JNI_TRUE : JNI_FALSE; + } +#endif // USE_OPENGL_RENDERER + bitmap->setPixels(NULL, NULL); + return JNI_TRUE; +} + +static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint width, jint height, jint configHandle, jint allocSize) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); + if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) { + // done in native as there's no way to get BytesPerPixel in Java + doThrowIAE(env, "Bitmap not large enough to support new configuration"); + return; + } + SkPixelRef* ref = bitmap->pixelRef(); + SkSafeRef(ref); + bitmap->setConfig(config, width, height); + bitmap->setPixelRef(ref); + + // notifyPixelsChanged will increment the generation ID even though the actual pixel data + // hasn't been touched. This signals the renderer that the bitmap (including width, height, + // and config) has changed. + ref->notifyPixelsChanged(); + SkSafeUnref(ref); +} + +// These must match the int values in Bitmap.java +enum JavaEncodeFormat { + kJPEG_JavaEncodeFormat = 0, + kPNG_JavaEncodeFormat = 1, + kWEBP_JavaEncodeFormat = 2 +}; + +static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint format, jint quality, + jobject jstream, jbyteArray jstorage) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkImageEncoder::Type fm; + + switch (format) { + case kJPEG_JavaEncodeFormat: + fm = SkImageEncoder::kJPEG_Type; + break; + case kPNG_JavaEncodeFormat: + fm = SkImageEncoder::kPNG_Type; + break; + case kWEBP_JavaEncodeFormat: + fm = SkImageEncoder::kWEBP_Type; + break; + default: + return JNI_FALSE; + } + + bool success = false; + if (NULL != bitmap) { + SkAutoLockPixels alp(*bitmap); + + if (NULL == bitmap->getPixels()) { + return JNI_FALSE; + } + + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + if (NULL == strm) { + return JNI_FALSE; + } + + SkImageEncoder* encoder = SkImageEncoder::Create(fm); + if (NULL != encoder) { + success = encoder->encodeStream(strm, *bitmap, quality); + delete encoder; + } + delete strm; + } + return success ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + bitmap->eraseColor(color); +} + +static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + return static_cast<jint>(bitmap->rowBytes()); +} + +static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + return static_cast<jint>(bitmap->config()); +} + +static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + return static_cast<jint>(bitmap->getGenerationID()); +} + +static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasAlpha, jboolean isPremul) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + if (!hasAlpha) { + bitmap->setAlphaType(kOpaque_SkAlphaType); + } else if (isPremul) { + bitmap->setAlphaType(kPremul_SkAlphaType); + } else { + bitmap->setAlphaType(kUnpremul_SkAlphaType); + } +} + +static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasMipMap) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + bitmap->setHasHardwareMipMap(hasMipMap); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { + if (parcel == NULL) { + SkDebugf("-------- unparcel parcel is NULL\n"); + return NULL; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + const bool isMutable = p->readInt32() != 0; + const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); + const int width = p->readInt32(); + const int height = p->readInt32(); + const int rowBytes = p->readInt32(); + const int density = p->readInt32(); + + if (SkBitmap::kARGB_8888_Config != config && + SkBitmap::kRGB_565_Config != config && + SkBitmap::kARGB_4444_Config != config && + SkBitmap::kIndex8_Config != config && + SkBitmap::kA8_Config != config) { + SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config); + return NULL; + } + + SkBitmap* bitmap = new SkBitmap; + + bitmap->setConfig(config, width, height, rowBytes); + + SkColorTable* ctable = NULL; + if (config == SkBitmap::kIndex8_Config) { + int count = p->readInt32(); + if (count > 0) { + size_t size = count * sizeof(SkPMColor); + const SkPMColor* src = (const SkPMColor*)p->readInplace(size); + ctable = new SkColorTable(src, count); + } + } + + jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); + if (NULL == buffer) { + SkSafeUnref(ctable); + delete bitmap; + return NULL; + } + + SkSafeUnref(ctable); + + size_t size = bitmap->getSize(); + + android::Parcel::ReadableBlob blob; + android::status_t status = p->readBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not read bitmap from parcel blob."); + delete bitmap; + return NULL; + } + + bitmap->lockPixels(); + memcpy(bitmap->getPixels(), blob.data(), size); + bitmap->unlockPixels(); + + blob.release(); + + return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable), + NULL, NULL, density); +} + +static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, + jlong bitmapHandle, + jboolean isMutable, jint density, + jobject parcel) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + if (parcel == NULL) { + SkDebugf("------- writeToParcel null parcel\n"); + return JNI_FALSE; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + p->writeInt32(isMutable); + p->writeInt32(bitmap->config()); + p->writeInt32(bitmap->width()); + p->writeInt32(bitmap->height()); + p->writeInt32(bitmap->rowBytes()); + p->writeInt32(density); + + if (bitmap->config() == SkBitmap::kIndex8_Config) { + SkColorTable* ctable = bitmap->getColorTable(); + if (ctable != NULL) { + int count = ctable->count(); + p->writeInt32(count); + memcpy(p->writeInplace(count * sizeof(SkPMColor)), + ctable->lockColors(), count * sizeof(SkPMColor)); + ctable->unlockColors(); + } else { + p->writeInt32(0); // indicate no ctable + } + } + + size_t size = bitmap->getSize(); + + android::Parcel::WritableBlob blob; + android::status_t status = p->writeBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not write bitmap to parcel blob."); + return JNI_FALSE; + } + + bitmap->lockPixels(); + const void* pSrc = bitmap->getPixels(); + if (pSrc == NULL) { + memset(blob.data(), 0, size); + } else { + memcpy(blob.data(), pSrc, size); + } + bitmap->unlockPixels(); + + blob.release(); + return JNI_TRUE; +} + +static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, + jlong srcHandle, jlong paintHandle, + jintArray offsetXY) { + const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + SkIPoint offset; + SkBitmap* dst = new SkBitmap; + JavaPixelAllocator allocator(env); + + src->extractAlpha(dst, paint, &allocator, &offset); + // If Skia can't allocate pixels for destination bitmap, it resets + // it, that is set its pixels buffer to NULL, and zero width and height. + if (dst->getPixels() == NULL && src->getPixels() != NULL) { + delete dst; + doThrowOOME(env, "failed to allocate pixels for alpha"); + return NULL; + } + if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { + int* array = env->GetIntArrayElements(offsetXY, NULL); + array[0] = offset.fX; + array[1] = offset.fY; + env->ReleaseIntArrayElements(offsetXY, array, 0); + } + + return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), + getPremulBitmapCreateFlags(true), NULL, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y, jboolean isPremultiplied) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkAutoLockPixels alp(*bitmap); + + ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); + if (NULL == proc) { + return 0; + } + const void* src = bitmap->getAddr(x, y); + if (NULL == src) { + return 0; + } + + SkColor dst[1]; + proc(dst, src, 1, bitmap->getColorTable()); + return static_cast<jint>(dst[0]); +} + +static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height, jboolean isPremultiplied) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkAutoLockPixels alp(*bitmap); + + ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); + if (NULL == proc) { + return; + } + const void* src = bitmap->getAddr(x, y); + if (NULL == src) { + return; + } + + SkColorTable* ctable = bitmap->getColorTable(); + jint* dst = env->GetIntArrayElements(pixelArray, NULL); + SkColor* d = (SkColor*)dst + offset; + while (--height >= 0) { + proc(d, src, width, ctable); + d += stride; + src = (void*)((const char*)src + bitmap->rowBytes()); + } + env->ReleaseIntArrayElements(pixelArray, dst, 0); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y, jint colorHandle, jboolean isPremultiplied) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkColor color = static_cast<SkColor>(colorHandle); + SkAutoLockPixels alp(*bitmap); + if (NULL == bitmap->getPixels()) { + return; + } + + FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied); + if (NULL == proc) { + return; + } + + proc(bitmap->getAddr(x, y), &color, 1, x, y); + bitmap->notifyPixelsChanged(); +} + +static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height, jboolean isPremultiplied) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + GraphicsJNI::SetPixels(env, pixelArray, offset, stride, + x, y, width, height, *bitmap, isPremultiplied); +} + +static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkAutoLockPixels alp(*bitmap); + const void* src = bitmap->getPixels(); + + if (NULL != src) { + android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); + + // the java side has already checked that buffer is large enough + memcpy(abp.pointer(), src, bitmap->getSize()); + } +} + +static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkAutoLockPixels alp(*bitmap); + void* dst = bitmap->getPixels(); + + if (NULL != dst) { + android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); + // the java side has already checked that buffer is large enough + memcpy(dst, abp.pointer(), bitmap->getSize()); + bitmap->notifyPixelsChanged(); + } +} + +static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, + jlong bm1Handle) { + const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle); + const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle); + if (bm0->width() != bm1->width() || + bm0->height() != bm1->height() || + bm0->config() != bm1->config()) { + return JNI_FALSE; + } + + SkAutoLockPixels alp0(*bm0); + SkAutoLockPixels alp1(*bm1); + + // if we can't load the pixels, return false + if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { + return JNI_FALSE; + } + + if (bm0->config() == SkBitmap::kIndex8_Config) { + SkColorTable* ct0 = bm0->getColorTable(); + SkColorTable* ct1 = bm1->getColorTable(); + if (NULL == ct0 || NULL == ct1) { + return JNI_FALSE; + } + if (ct0->count() != ct1->count()) { + return JNI_FALSE; + } + + SkAutoLockColors alc0(ct0); + SkAutoLockColors alc1(ct1); + const size_t size = ct0->count() * sizeof(SkPMColor); + if (memcmp(alc0.colors(), alc1.colors(), size) != 0) { + return JNI_FALSE; + } + } + + // now compare each scanline. We can't do the entire buffer at once, + // since we don't care about the pixel values that might extend beyond + // the width (since the scanline might be larger than the logical width) + const int h = bm0->height(); + const size_t size = bm0->width() * bm0->bytesPerPixel(); + for (int y = 0; y < h; y++) { + if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) { + return JNI_FALSE; + } + } + return JNI_TRUE; +} + +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + bitmap->lockPixels(); + bitmap->unlockPixels(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gBitmapMethods[] = { + { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", + (void*)Bitmap_creator }, + { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", + (void*)Bitmap_copy }, + { "nativeDestructor", "(J)V", (void*)Bitmap_destructor }, + { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, + { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure }, + { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", + (void*)Bitmap_compress }, + { "nativeErase", "(JI)V", (void*)Bitmap_erase }, + { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, + { "nativeConfig", "(J)I", (void*)Bitmap_config }, + { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, + { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied}, + { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, + { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, + { "nativeCreateFromParcel", + "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", + (void*)Bitmap_createFromParcel }, + { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z", + (void*)Bitmap_writeToParcel }, + { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", + (void*)Bitmap_extractAlpha }, + { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, + { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel }, + { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels }, + { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel }, + { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels }, + { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsToBuffer }, + { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsFromBuffer }, + { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, +}; + +#define kClassPathName "android/graphics/Bitmap" + +int register_android_graphics_Bitmap(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, + gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods)); +} diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp index 70e2db5..cfa9cd8 100644 --- a/core/jni/android/graphics/CanvasProperty.cpp +++ b/core/jni/android/graphics/CanvasProperty.cpp @@ -18,7 +18,7 @@ #include "GraphicsJNI.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/VirtualLightRefBase.h> +#include <utils/RefBase.h> #include <CanvasProperty.h> namespace android { @@ -27,22 +27,13 @@ using namespace uirenderer; #ifdef USE_OPENGL_RENDERER -static jlong incRef(VirtualLightRefBase* ptr) { - ptr->incStrong(0); - return reinterpret_cast<jlong>(ptr); -} - static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) { - return incRef(new CanvasPropertyPrimitive(initialValue)); + return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue)); } static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) { const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr); - return incRef(new CanvasPropertyPaint(*paint)); -} - -static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) { - reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0); + return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint)); } #endif @@ -57,7 +48,6 @@ static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER { "nCreateFloat", "(F)J", (void*) createFloat }, { "nCreatePaint", "(J)J", (void*) createPaint }, - { "nUnref", "(J)V", (void*) unref }, #endif }; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index da752752..463a0a8 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -526,12 +526,15 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da switch (format) { default: - // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT - // and AUDIO_FORMAT_PCM_8_BIT, due to the limited set of values for audioFormat. + // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT, + // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT, + // due to the limited set of values for audioFormat. // The next section of the switch will probably work for more formats, but it has only - // been tested for AUDIO_FORMAT_PCM_16_BIT, so that's why the "default" case fails. + // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT, + // so that's why the "default" case fails. break; + case AUDIO_FORMAT_PCM_FLOAT: case AUDIO_FORMAT_PCM_16_BIT: { // writing to shared memory, check for capacity if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { @@ -679,6 +682,44 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, // ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz, + jfloatArray javaAudioData, + jint offsetInFloats, jint sizeInFloats, + jint javaAudioFormat, + jboolean isWriteBlocking) { + + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for write()"); + return 0; + } + + jfloat* cAudioData = NULL; + if (javaAudioData) { + cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); + if (cAudioData == NULL) { + ALOGE("Error retrieving source of audio data to play, can't play"); + return 0; // out of memory or no data to load + } + } else { + ALOGE("NULL java array of audio data to play, can't play"); + return 0; + } + jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, + offsetInFloats * sizeof(float), sizeInFloats * sizeof(float), + isWriteBlocking == JNI_TRUE /* blocking */); + env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0); + + if (written > 0) { + written /= sizeof(float); + } + + return written; +} + + +// ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { @@ -963,6 +1004,7 @@ static JNINativeMethod gMethods[] = { "(Ljava/lang/Object;IIIZ)I", (void *)android_media_AudioTrack_write_native_bytes}, {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, + {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_write_float}, {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, {"native_get_native_frame_count", "()I", (void *)android_media_AudioTrack_get_native_frame_count}, diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index 3be013b..4787d28 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -101,7 +101,6 @@ static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis, RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw); BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue); - animator->incStrong(0); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } @@ -111,7 +110,6 @@ static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz, RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw); CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr); BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue); - animator->incStrong(0); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } @@ -124,7 +122,6 @@ static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw); BaseAnimator* animator = new CanvasPropertyPaintAnimator( canvasProperty, paintField, deltaType, deltaValue); - animator->incStrong(0); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } @@ -135,11 +132,6 @@ static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint dura animator->setDuration(duration); } -static void unref(JNIEnv* env, jobject clazz, jlong objPtr) { - VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr); - obj->decStrong(0); -} - #endif // ---------------------------------------------------------------------------- @@ -154,7 +146,6 @@ static JNINativeMethod gMethods[] = { { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator }, { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator }, { "nSetDuration", "(JI)V", (void*) setDuration }, - { "nUnref", "(J)V", (void*) unref }, #endif }; diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp new file mode 100644 index 0000000..ce6f207 --- /dev/null +++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +namespace android { + +static void incStrong(JNIEnv* env, jobject clazz, jlong objPtr) { + VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr); + obj->incStrong(0); +} + +static void decStrong(JNIEnv* env, jobject clazz, jlong objPtr) { + VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr); + obj->decStrong(0); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr"; + +static JNINativeMethod gMethods[] = { + { "nIncStrong", "(J)V", (void*) incStrong }, + { "nDecStrong", "(J)V", (void*) decStrong }, +}; + +int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv* env) { + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1880a15 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..aecb4d2 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..8401f91 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..5832865 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..6d14962 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..aee057c --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..fb5801e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..fdb5271 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b8c7397 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..d0395a8 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..59bb437 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c053b90 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..eb30a79 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1af0bff --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..3b36e7d --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c12d20a --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..882365b --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..f6c7094 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0e326c9 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..8bf1170 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..cedb66e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..257d7ba --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..e07b36e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ef94200 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ad67004 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..50796e2 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ba7be9e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..bdbfe78 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..fe89951 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..840c88f --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..621d1d2 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..fd8be89 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png Binary files differindex f1023ea..5a99528 100644 --- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png Binary files differindex 15ceeee..79de664 100644 --- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png Binary files differindex 90b1498..73e8f1c 100644 --- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png Binary files differindex b535758..ff6affe 100644 --- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0f44ff9 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..9d5dda0 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..e4ce802 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..d1806ac --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ab9315b --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..46e90e6 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..e8c56ff --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..59dcb7e --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..e9bd4a2 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1d05037 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..91b40de --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c531cab --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..11bb387 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..8843210 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..6ff2f3d --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..a03c1e2 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0a22e1a --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..2e2469c --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c1054d9 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..cf8d80a --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..9d9e870 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1bad701 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..a84a54f --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..4d8050b --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..374172c --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..233036e --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..61d9b58 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..274e983 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..acf16e5 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ee48241 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..dbbb736 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..bcabd0d --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png Binary files differindex 1833704..e40cba8 100644 --- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png Binary files differindex e64d3f2..437a3e3 100644 --- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png Binary files differindex ffd6c39..8949b52 100644 --- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png Binary files differindex 15faff0..d727683 100644 --- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml index 37df348..49028b4 100644 --- a/core/res/res/drawable-nodpi/stat_sys_adb.xml +++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml @@ -21,7 +21,7 @@ <group> <path android:name="adb" - android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z" + android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z" android:fill="#FFFFFFFF" /> </group> diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..25500e8 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b136e25 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..6a94e30 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..d386421 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c811385 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..58b3267 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0659e72 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b4227d1 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..714ef00 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..139595b --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..4491107 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..20eb752 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..532c9f2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0d78a32 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..af29678 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..23eb9e3 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..cd11e14 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b10db83 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..efeb6fb --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..83080af --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b9cc322 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..3b5f9c4 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..58c93db --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0f1d010 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..05a7a0f --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..9345035 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..5f149b7 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..191f369 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..44e08e6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..5a9dfa0 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ee921c6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..567bb0c --- /dev/null +++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png Binary files differindex ad72f06..729e0bf 100644 --- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png Binary files differindex 7aceed1..d018a7c 100644 --- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png Binary files differindex 309b528..a7a972c 100644 --- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png Binary files differindex 139795e..dd8910b 100644 --- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1881f54 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..6f8ec2d --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..c954ed9 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..9d1a47e --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..ce63631 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..430c134 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..cdebf83 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..40ceadb --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..fb13eb2 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..d716fba --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..b8be041 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..bad0c3c --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..a6368fb --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..234e5d1 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..3e7796d --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0673999 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..4779944 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..866f7b7 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..76aae57 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0cd470a --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..0015b39 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..2f69f5b --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..77142fd --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..2f81277 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..d37fe60 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..cb62079 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..82dc428 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..2cba2fb --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..5de1952 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..1c22a17 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..7f652fc --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png Binary files differnew file mode 100644 index 0000000..076acbd --- /dev/null +++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png Binary files differindex c11b0ae..a2b5716 100644 --- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png Binary files differindex cde797e..caabc2c 100644 --- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png Binary files differindex 9e234af..8d79a13 100644 --- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png Binary files differindex b371eab..e0e4ef9 100644 --- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml index 4b329ad..96715a4 100644 --- a/core/res/res/drawable/btn_check_quantum_anim.xml +++ b/core/res/res/drawable/btn_check_quantum_anim.xml @@ -1,3 +1,4 @@ +<?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"); @@ -13,21 +14,92 @@ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:versionCode="1" > +<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:state_checked="true"> + <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" + android:alpha="?attr/disabledAlpha" /> + </item> + <item android:state_enabled="false"> + <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha" + android:tint="?attr/colorControlNormal" + android:alpha="?attr/disabledAlpha" /> + </item> + <item android:state_checked="true" android:id="@+id/on"> + <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:id="@+id/off"> + <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha" + android:tint="?attr/colorControlNormal" /> + </item> + <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true"> + <animation-list> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00001_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00002_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00003_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00004_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00005_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00006_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00007_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00008_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00009_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00010_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00011_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00012_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00013_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00014_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + </animation-list> + </transition> +</animated-selector> - <size - android:height="32dp" - android:width="32dp" /> - - <viewport - android:viewportHeight="320" - android:viewportWidth="320" /> - - <group> - <path - android:name="check" - android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z" - android:fill="?attr/colorControlActivated" /> - </group> -</vector> diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml new file mode 100644 index 0000000..5068b7a --- /dev/null +++ b/core/res/res/drawable/btn_radio_quantum_anim.xml @@ -0,0 +1,104 @@ +<?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. +--> + +<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:state_checked="true"> + <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" + android:alpha="?attr/disabledAlpha" /> + </item> + <item android:state_enabled="false"> + <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha" + android:tint="?attr/colorControlNormal" + android:alpha="?attr/disabledAlpha" /> + </item> + <item android:state_checked="true" android:id="@+id/on"> + <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:id="@+id/off"> + <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha" + android:tint="?attr/colorControlNormal" /> + </item> + <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true"> + <animation-list> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00001_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00002_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00003_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00004_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00005_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00006_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00007_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00008_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00009_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00010_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00011_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00012_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00013_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00014_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + <item android:duration="33"> + <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </item> + </animation-list> + </transition> +</animated-selector> diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml index d172b05..f82fe7a 100644 --- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml +++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml @@ -16,22 +16,26 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false"> - <bitmap android:src="@drawable/scrubber_track_qntm_alpha" + <nine-patch android:src="@drawable/scrubber_track_qntm_alpha" android:tint="?attr/colorControlNormal" /> </item> <item> <layer-list> <item android:id="@id/background"> - <bitmap android:src="@drawable/scrubber_track_qntm_alpha" + <nine-patch android:src="@drawable/scrubber_track_qntm_alpha" android:tint="?attr/colorControlNormal" /> </item> <item android:id="@id/secondaryProgress"> - <bitmap android:src="@drawable/scrubber_primary_qntm_alpha" - android:tint="?attr/colorControlNormal" /> + <scale android:scaleWidth="100%"> + <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha" + android:tint="?attr/colorControlNormal" /> + </scale> </item> <item android:id="@id/progress"> - <bitmap android:src="@drawable/scrubber_primary_qntm_alpha" - android:tint="?attr/colorControlActivated" /> + <scale android:scaleWidth="100%"> + <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </scale> </item> </layer-list> </item> diff --git a/core/res/res/layout/alert_dialog_micro.xml b/core/res/res/layout/alert_dialog_micro.xml index f8eb46c..abdbd16 100644 --- a/core/res/res/layout/alert_dialog_micro.xml +++ b/core/res/res/layout/alert_dialog_micro.xml @@ -20,6 +20,8 @@ android:id="@+id/parentPanel" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@android:color/white" + android:layout_gravity="center" android:orientation="vertical"> <LinearLayout android:id="@+id/topPanel" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d9473ec..cedb92d 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3343,6 +3343,8 @@ <attr name="thumb" format="reference" /> <!-- An offset for the thumb that allows it to extend out of the range of the track. --> <attr name="thumbOffset" format="dimension" /> + <!-- Whether to split the track and leave a gap for the thumb drawable. --> + <attr name="splitTrack" format="boolean" /> </declare-styleable> <declare-styleable name="StackView"> @@ -4239,6 +4241,58 @@ <attr name="autoMirrored"/> </declare-styleable> + <!-- Drawable used to render several states with animated transitions. Each state + is represented by a child drawable with an optional keyframe ID. --> + <declare-styleable name="AnimatedStateListDrawable"> + <!-- Indicates whether the drawable should be initially visible. --> + <attr name="visible" /> + <!-- If true, allows the drawable's padding to change based on the + current state that is selected. If false, the padding will + stay the same (based on the maximum padding of all the states). + Enabling this feature requires that the owner of the drawable + deal with performing layout when the state changes, which is + often not supported. --> + <attr name="variablePadding" /> + <!-- If true, the drawable's reported internal size will remain + constant as the state changes; the size is the maximum of all + of the states. If false, the size will vary based on the + current state. --> + <attr name="constantSize" /> + <!-- Enables or disables dithering of the bitmap if the bitmap does not have the + same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with + an RGB 565 screen). --> + <attr name="dither" /> + <!-- Amount of time (in milliseconds) to fade in a new state drawable. --> + <attr name="enterFadeDuration" /> + <!-- Amount of time (in milliseconds) to fade out an old state drawable. --> + <attr name="exitFadeDuration" /> + <!-- Indicates if the drawable needs to be mirrored when its layout direction is + RTL (right-to-left). --> + <attr name="autoMirrored"/> + </declare-styleable> + + <!-- Transition used to animate between states with keyframe IDs. --> + <declare-styleable name="AnimatedStateListDrawableItem"> + <!-- Reference to a drawable resource to use for the frame. If not + given, the drawable must be defined by the first child tag. --> + <attr name="drawable" /> + <!-- Keyframe identifier for use in specifying transitions. --> + <attr name="id" /> + </declare-styleable> + + <!-- Transition used to animate between states with keyframe IDs. --> + <declare-styleable name="AnimatedStateListDrawableTransition"> + <!-- Keyframe identifier for the starting state. --> + <attr name="fromId" format="reference" /> + <!-- Keyframe identifier for the ending state. --> + <attr name="toId" format="reference" /> + <!-- Reference to a animation drawable resource to use for the frame. If not + given, the animation drawable must be defined by the first child tag. --> + <attr name="drawable" /> + <!-- Whether this transition is reversible. --> + <attr name="reversible" format="boolean" /> + </declare-styleable> + <!-- Drawable used to render several animated frames. --> <declare-styleable name="AnimationDrawable"> <attr name="visible" /> @@ -6341,6 +6395,8 @@ <attr name="switchMinWidth" format="dimension" /> <!-- Minimum space between the switch and caption text --> <attr name="switchPadding" format="dimension" /> + <!-- Whether to split the track and leave a gap for the thumb drawable. --> + <attr name="splitTrack" /> </declare-styleable> <declare-styleable name="Pointer"> @@ -6553,4 +6609,8 @@ <attr name="layout_gravity" /> </declare-styleable> + <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. --> + <declare-styleable name="EdgeEffect"> + <attr name="colorPrimaryLight" /> + </declare-styleable> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f39155b..83cbb74 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1393,8 +1393,10 @@ <item>com.android.inputmethod.latin</item> </string-array> - <string-array name="config_notificationScorers"> - <item>com.android.internal.notification.PeopleNotificationScorer</item> + <!-- The list of classes that should be added to the notification ranking pipline. + See {@link com.android.server.notification.NotificationSignalExtractortor} --> + <string-array name="config_notificationSignalExtractors"> + <item>com.android.server.notification.ValidateNotificationPeople</item> </string-array> <!-- Flag indicating that this device does not rotate and will always remain in its default diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 6b2c788..bf92f9b 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -143,6 +143,8 @@ <!-- Preferred width of the search view. --> <dimen name="search_view_preferred_width">320dip</dimen> + <!-- Dialog padding for round display --> + <dimen name="alert_dialog_round_padding">27dip</dimen> <!-- Dialog title height --> <dimen name="alert_dialog_title_height">64dip</dimen> <!-- Dialog button bar height --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 4e584c0..dc5efea 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2166,6 +2166,10 @@ <public type="attr" name="documentLaunchMode" /> <public type="attr" name="autoRemoveFromRecents" /> <public type="attr" name="stateListAnimator" /> + <public type="attr" name="toId" /> + <public type="attr" name="fromId" /> + <public type="attr" name="reversible" /> + <public type="attr" name="splitTrack" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml index bdaa49d..5bac1f9 100644 --- a/core/res/res/values/styles_micro.xml +++ b/core/res/res/values/styles_micro.xml @@ -15,6 +15,16 @@ --> <resources> <style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light"> + <item name="fullDark">@null</item> + <item name="topDark">@null</item> + <item name="centerDark">@null</item> + <item name="bottomDark">@null</item> + <item name="fullBright">@null</item> + <item name="topBright">@null</item> + <item name="centerBright">@null</item> + <item name="bottomBright">@null</item> + <item name="bottomMedium">@null</item> + <item name="centerMedium">@null</item> <item name="layout">@layout/alert_dialog_micro</item> </style> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 88a2a9f..e693673 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -455,10 +455,10 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.CompoundButton.Switch"> <item name="track">@drawable/switch_track_quantum</item> <item name="thumb">@drawable/switch_inner_quantum</item> + <item name="splitTrack">true</item> <item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item> <item name="textOn"></item> <item name="textOff"></item> - <item name="thumbTextPadding">12dip</item> <item name="switchMinWidth">72dip</item> <item name="switchPadding">16dip</item> <item name="background">?attr/selectableItemBackground</item> @@ -572,10 +572,8 @@ please see styles_device_defaults.xml. <item name="indeterminateOnly">false</item> <item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item> <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item> - <item name="minHeight">13dip</item> - <item name="maxHeight">13dip</item> <item name="thumb">@drawable/scrubber_control_selector_quantum</item> - <item name="thumbOffset">16dip</item> + <item name="splitTrack">true</item> <item name="focusable">true</item> <item name="paddingStart">16dip</item> <item name="paddingEnd">16dip</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2bf72e8..1057cc2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -120,6 +120,7 @@ <java-symbol type="id" name="overlay_display_window_title" /> <java-symbol type="id" name="package_label" /> <java-symbol type="id" name="packages_list" /> + <java-symbol type="id" name="parentPanel" /> <java-symbol type="id" name="pause" /> <java-symbol type="id" name="perms_list" /> <java-symbol type="id" name="perm_icon" /> @@ -325,6 +326,7 @@ <java-symbol type="color" name="tab_indicator_text_v4" /> <java-symbol type="dimen" name="accessibility_touch_slop" /> + <java-symbol type="dimen" name="alert_dialog_round_padding"/> <java-symbol type="dimen" name="config_prefDialogWidth" /> <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" /> <java-symbol type="dimen" name="config_viewMinFlingVelocity" /> @@ -1653,7 +1655,7 @@ <java-symbol type="id" name="button_always" /> <java-symbol type="integer" name="config_globalActionsKeyTimeout" /> <java-symbol type="integer" name="config_maxResolverActivityColumns" /> - <java-symbol type="array" name="config_notificationScorers" /> + <java-symbol type="array" name="config_notificationSignalExtractors" /> <java-symbol type="layout" name="notification_quantum_action" /> <java-symbol type="layout" name="notification_quantum_action_list" /> diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml index 39df700..9647947 100644 --- a/core/res/res/values/themes_micro.xml +++ b/core/res/res/values/themes_micro.xml @@ -47,10 +47,15 @@ <item name="textAppearanceInverse">@style/TextAppearance.Micro</item> </style> - <style name="Theme.Micro.Dialog.Alert" parent="Theme.Holo.Light.Dialog.Alert"> + <style name="Theme.Micro.Dialog.Alert"> <item name="windowTitleStyle">@style/DialogWindowTitle.Micro</item> <item name="alertDialogStyle">@style/AlertDialog.Micro</item> <item name="windowIsFloating">false</item> + <item name="windowBackground">@android:color/transparent</item> + <item name="windowOverscan">true</item> + <item name="windowContentOverlay">@null</item> + <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item> + <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item> </style> <style name="Theme.Micro.Dialog.AppError" parent="Theme.Micro.Dialog"> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index 768fd9a..18d6f80 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -122,8 +122,8 @@ please see themes_device_defaults.xml. <item name="listDivider">@drawable/list_divider_quantum</item> <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item> - <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item> - <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item> + <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item> + <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item> <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item> @@ -466,8 +466,8 @@ please see themes_device_defaults.xml. <item name="listDivider">@drawable/list_divider_quantum</item> <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.Light.TextView.ListSeparator</item> - <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item> - <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item> + <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item> + <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item> <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item> diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh index 9029ba5..e0f4f6d 100755 --- a/core/tests/inputmethodtests/run_core_inputmethod_test.sh +++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh @@ -21,4 +21,4 @@ if [[ $rebuild == true ]]; then $COMMAND fi -adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner +adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest,android.os.CursorAnchorInfoTest,android.os.SparseRectFArrayTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java new file mode 100644 index 0000000..59a6314 --- /dev/null +++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java @@ -0,0 +1,164 @@ +/* + * 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.os; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.CursorAnchorInfo; +import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder; + +public class CursorAnchorInfoTest extends InstrumentationTestCase { + // null represents a character that is invisible, for example because it's overlapped by some + // other UI elements. + private static final RectF[] MANY_RECTS = new RectF[] { + null, + new RectF(102.0f, 202.0f, 302.0f, 402.0f), + new RectF(103.0f, 203.0f, 303.0f, 403.0f), + new RectF(104.0f, 204.0f, 304.0f, 404.0f), + new RectF(105.0f, 205.0f, 305.0f, 405.0f), + new RectF(106.0f, 206.0f, 306.0f, 406.0f), + null, + new RectF(108.0f, 208.0f, 308.0f, 408.0f), + new RectF(109.0f, 209.0f, 309.0f, 409.0f), + new RectF(110.0f, 210.0f, 310.0f, 410.0f), + new RectF(111.0f, 211.0f, 311.0f, 411.0f), + new RectF(112.0f, 212.0f, 312.0f, 412.0f), + new RectF(113.0f, 213.0f, 313.0f, 413.0f), + new RectF(114.0f, 214.0f, 314.0f, 414.0f), + new RectF(115.0f, 215.0f, 315.0f, 415.0f), + new RectF(116.0f, 216.0f, 316.0f, 416.0f), + new RectF(117.0f, 217.0f, 317.0f, 417.0f), + null, + null, + }; + + @SmallTest + public void testBuilder() throws Exception { + final int SELECTION_START = 30; + final int SELECTION_END = 40; + final int CANDIDATES_START = 32; + final int CANDIDATES_END = 33; + final float INSERTION_MARKER_HORIZONTAL = 10.5f; + final float INSERTION_MARKER_TOP = 100.1f; + final float INSERTION_MARKER_BASELINE = 110.4f; + final float INSERTION_MARKER_BOTOM = 111.0f; + Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX); + TRANSFORM_MATRIX.setScale(10.0f, 20.0f); + + final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder(); + builder.setSelectionRange(SELECTION_START, SELECTION_END) + .setCandidateRange(CANDIDATES_START, CANDIDATES_END) + .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP, + INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM) + .setMatrix(TRANSFORM_MATRIX); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + if (rect != null) { + builder.addCharacterRect(i, rect.left, rect.top, rect.right, rect.bottom); + } + } + + final CursorAnchorInfo info = builder.build(); + assertEquals(SELECTION_START, info.getSelectionStart()); + assertEquals(SELECTION_END, info.getSelectionEnd()); + assertEquals(CANDIDATES_START, info.getCandidatesStart()); + assertEquals(CANDIDATES_END, info.getCandidatesEnd()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal()); + assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop()); + assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline()); + assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom()); + assertEquals(TRANSFORM_MATRIX, info.getMatrix()); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, info.getCharacterRect(i)); + } + + // Make sure that the builder can reproduce the same object. + final CursorAnchorInfo info2 = builder.build(); + assertEquals(SELECTION_START, info2.getSelectionStart()); + assertEquals(SELECTION_END, info2.getSelectionEnd()); + assertEquals(CANDIDATES_START, info2.getCandidatesStart()); + assertEquals(CANDIDATES_END, info2.getCandidatesEnd()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal()); + assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop()); + assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline()); + assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom()); + assertEquals(TRANSFORM_MATRIX, info2.getMatrix()); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, info2.getCharacterRect(i)); + } + assertEquals(info, info2); + assertEquals(info.hashCode(), info2.hashCode()); + + // Make sure that object can be marshalled via {@link Parsel}. + final CursorAnchorInfo info3 = cloneViaParcel(info2); + assertEquals(SELECTION_START, info3.getSelectionStart()); + assertEquals(SELECTION_END, info3.getSelectionEnd()); + assertEquals(CANDIDATES_START, info3.getCandidatesStart()); + assertEquals(CANDIDATES_END, info3.getCandidatesEnd()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal()); + assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop()); + assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline()); + assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom()); + assertEquals(TRANSFORM_MATRIX, info3.getMatrix()); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, info3.getCharacterRect(i)); + } + assertEquals(info.hashCode(), info3.hashCode()); + + builder.reset(); + final CursorAnchorInfo uninitializedInfo = builder.build(); + assertEquals(-1, uninitializedInfo.getSelectionStart()); + assertEquals(-1, uninitializedInfo.getSelectionEnd()); + assertEquals(-1, uninitializedInfo.getCandidatesStart()); + assertEquals(-1, uninitializedInfo.getCandidatesEnd()); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal()); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop()); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline()); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom()); + assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix()); + } + + @SmallTest + public void testBuilderAdd() throws Exception { + // A negative index should be rejected. + try { + new CursorAnchorInfoBuilder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f); + } catch (IllegalArgumentException ex) { + assertTrue(true); + } + } + + private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) { + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + src.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return new CursorAnchorInfo(parcel); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } +} + diff --git a/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java b/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java new file mode 100644 index 0000000..fae7230 --- /dev/null +++ b/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java @@ -0,0 +1,233 @@ +/* + * 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.os; + +import android.graphics.RectF; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.SparseRectFArray; +import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder; + +import java.util.Objects; + +public class SparseRectFArrayTest extends InstrumentationTestCase { + // A test data for {@link SparseRectFArray}. null represents the gap of indices. + private static final RectF[] MANY_RECTS = new RectF[] { + null, + new RectF(102.0f, 202.0f, 302.0f, 402.0f), + new RectF(103.0f, 203.0f, 303.0f, 403.0f), + new RectF(104.0f, 204.0f, 304.0f, 404.0f), + new RectF(105.0f, 205.0f, 305.0f, 405.0f), + new RectF(106.0f, 206.0f, 306.0f, 406.0f), + null, + new RectF(108.0f, 208.0f, 308.0f, 408.0f), + new RectF(109.0f, 209.0f, 309.0f, 409.0f), + new RectF(110.0f, 210.0f, 310.0f, 410.0f), + new RectF(111.0f, 211.0f, 311.0f, 411.0f), + new RectF(112.0f, 212.0f, 312.0f, 412.0f), + new RectF(113.0f, 213.0f, 313.0f, 413.0f), + new RectF(114.0f, 214.0f, 314.0f, 414.0f), + new RectF(115.0f, 215.0f, 315.0f, 415.0f), + new RectF(116.0f, 216.0f, 316.0f, 416.0f), + new RectF(117.0f, 217.0f, 317.0f, 417.0f), + null, + null, + new RectF(118.0f, 218.0f, 318.0f, 418.0f), + }; + + @SmallTest + public void testBuilder() throws Exception { + final RectF TEMP_RECT = new RectF(10.0f, 20.0f, 30.0f, 40.0f); + + final SparseRectFArrayBuilder builder = new SparseRectFArrayBuilder(); + builder.append(100, TEMP_RECT.left, TEMP_RECT.top, TEMP_RECT.right, TEMP_RECT.bottom); + assertNull(builder.build().get(-1)); + assertNull(builder.build().get(0)); + assertNull(builder.build().get(99)); + assertEquals(TEMP_RECT, builder.build().get(100)); + assertNull(builder.build().get(101)); + + // Test if {@link SparseRectFArrayBuilder#reset} resets its internal state. + builder.reset(); + assertNull(builder.build().get(100)); + + builder.reset(); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + if (rect != null) { + builder.append(i, rect.left, rect.top, rect.right, rect.bottom); + } + } + final SparseRectFArray array = builder.build(); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, array.get(i)); + } + + // Make sure the builder reproduces an equivalent object. + final SparseRectFArray array2 = builder.build(); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, array2.get(i)); + } + assertEqualRects(array, array2); + + // Make sure the instance can be marshaled via {@link Parcel}. + final SparseRectFArray array3 = cloneViaParcel(array); + for (int i = 0; i < MANY_RECTS.length; i++) { + final RectF rect = MANY_RECTS[i]; + assertEquals(rect, array3.get(i)); + } + assertEqualRects(array, array3); + + // Make sure the builder can be reset. + builder.reset(); + assertNull(builder.build().get(0)); + } + + @SmallTest + public void testEquality() throws Exception { + // Empty array should be equal. + assertEqualRects(new SparseRectFArrayBuilder().build(), + new SparseRectFArrayBuilder().build()); + + assertEqualRects( + new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(), + new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(), + new SparseRectFArrayBuilder().append(100, 2.0f, 2.0f, 3.0f, 4.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(), + new SparseRectFArrayBuilder().append(101, 1.0f, 2.0f, 3.0f, 4.0f).build()); + + assertEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 1.0f, 0.0f, 0.0f, 0.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 1.0f, 0.0f, 0.0f, 0.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(), + new SparseRectFArrayBuilder() + .append(100, 1.0f, 2.0f, 3.0f, 4.0f) + .append(102, 0.0f, 0.0f, 0.0f, 0.0f).build()); + + assertEqualRects( + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .append(1000, 0.0f, 0.0f, 0.0f, 0.0f) + .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f) + .build(), + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .append(1000, 0.0f, 0.0f, 0.0f, 0.0f) + .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f) + .build()); + + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .append(1000, 0.0f, 0.0f, 0.0f, 0.0f) + .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f) + .build(), + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .build()); + assertNotEqualRects( + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .append(1000, 0.0f, 0.0f, 0.0f, 0.0f) + .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f) + .build(), + new SparseRectFArrayBuilder() + .append(1, 1.0f, 2.0f, 3.0f, 4.0f) + .append(1000, 1.0f, 0.0f, 0.0f, 0.0f) + .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f) + .build()); + } + + @SmallTest + public void testBuilderAppend() throws Exception { + // Key should be appended in ascending order. + try { + new SparseRectFArrayBuilder().append(10, 0, 0, 0, 0).append(0, 1, 2, 3, 4); + } catch (IllegalArgumentException ex) { + assertTrue(true); + } + + try { + new SparseRectFArrayBuilder().append(10, 0, 0, 0, 0).append(10, 1, 2, 3, 4); + } catch (IllegalArgumentException ex) { + assertTrue(true); + } + } + + private static void assertEqualRects(SparseRectFArray a, SparseRectFArray b) { + assertEquals(a, b); + if (a != null && b != null) { + assertEquals(a.hashCode(), b.hashCode()); + } + } + + private static void assertNotEqualRects(SparseRectFArray a, SparseRectFArray b) { + assertFalse(Objects.equals(a, b)); + } + + private static SparseRectFArray cloneViaParcel(final SparseRectFArray src) { + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + src.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return new SparseRectFArray(parcel); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } +} diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index 4057736..6143b4b 100644 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -584,9 +584,14 @@ is sensitive to delays or lag in sound input or output.</td> </tr> <tr> <td><code>android.hardware.camera.any</code></td> - <td>The application uses at least one camera facing in any direction. Use this -in preference to <code>android.hardware.camera</code> if a back-facing camera is -not required.</td> + <td>The application uses at least one camera facing in any direction, or an +external camera device if one is connected. Use this in preference to +<code>android.hardware.camera</code> if a back-facing camera is not required. + </td> +</tr> +<tr> + <td><code>android.hardware.camera.external</code></td> + <td>The application uses an external camera device if one is connected.</td> </tr> <tr> diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd index 5a96ba1..e2326ec 100644 --- a/docs/html/guide/topics/resources/string-resource.jd +++ b/docs/html/guide/topics/resources/string-resource.jd @@ -20,9 +20,6 @@ your application with strings:</p> information about styling and formatting strings, see the section about <a href="#FormattingAndStyling">Formatting and Styling</a>.</p> - - - <h2 id="String">String</h2> <p>A single string that can be referenced from the application or from other resource files (such @@ -433,7 +430,7 @@ java.lang.Object...)">format</a>(res.getString(R.string.welcome_messages), usern -<h3>Styling with HTML markup</h3> +<h3 id="StylingWithHTML">Styling with HTML markup</h3> <p>You can add styling to your strings with HTML markup. For example:</p> <pre> @@ -497,5 +494,107 @@ java.lang.Object...)">format</a>(res.getString(R.string.welcome_messages), escap CharSequence styledText = Html.fromHtml(text); </pre> +<h2 id="StylingWithSpannables">Styling with Spannables</h2> +<p> +A {@link android.text.Spannable} is a text object that you can style with +typeface properties such as color and font weight. You use +{@link android.text.SpannableStringBuilder} to build +your text and then apply styles defined in the {@link android.text.style} +package to the text. +</p> + +<p>You can use the following helper methods to set up much of the work +of creating spannable text:</p> + +<pre style="pretty-print"> +/** + * Returns a CharSequence that concatenates the specified array of CharSequence + * objects and then applies a list of zero or more tags to the entire range. + * + * @param content an array of character sequences to apply a style to + * @param tags the styled span objects to apply to the content + * such as android.text.style.StyleSpan + * + */ +private static CharSequence apply(CharSequence[] content, Object... tags) { + SpannableStringBuilder text = new SpannableStringBuilder(); + openTags(text, tags); + for (CharSequence item : content) { + text.append(item); + } + closeTags(text, tags); + return text; +} + +/** + * Iterates over an array of tags and applies them to the beginning of the specified + * Spannable object so that future text appended to the text will have the styling + * applied to it. Do not call this method directly. + */ +private static void openTags(Spannable text, Object[] tags) { + for (Object tag : tags) { + text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK); + } +} + +/** + * "Closes" the specified tags on a Spannable by updating the spans to be + * endpoint-exclusive so that future text appended to the end will not take + * on the same styling. Do not call this method directly. + */ +private static void closeTags(Spannable text, Object[] tags) { + int len = text.length(); + for (Object tag : tags) { + if (len > 0) { + text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + text.removeSpan(tag); + } + } +} +</pre> +<p> +The following <code>bold</code>, <code>italic</code>, and <code>color</code> +methods show you how to call the helper methods to apply +styles defined in the {@link android.text.style} package. You +can create similar methods to do other types of text styling. +</p> + +<pre style="pretty-print"> +/** + * Returns a CharSequence that applies boldface to the concatenation + * of the specified CharSequence objects. + */ +public static CharSequence bold(CharSequence... content) { + return apply(content, new StyleSpan(Typeface.BOLD)); +} + +/** + * Returns a CharSequence that applies italics to the concatenation + * of the specified CharSequence objects. + */ +public static CharSequence italic(CharSequence... content) { + return apply(content, new StyleSpan(Typeface.ITALIC)); +} + +/** + * Returns a CharSequence that applies a foreground color to the + * concatenation of the specified CharSequence objects. + */ +public static CharSequence color(int color, CharSequence... content) { + return apply(content, new ForegroundColorSpan(color)); +} +</pre> +<p> +Here's an example of how to chain these methods to create a character sequence +with different types of styling applied to individual words: +</p> + +<pre style="pretty-print"> +// Create an italic "hello, " a red "world", +// and bold the entire sequence. +CharSequence text = bold(italic(res.getString(R.string.hello)), + color(Color.RED, res.getString(R.string.world))); +</pre>
\ No newline at end of file diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd index 3b1292e..59c2269 100644 --- a/docs/html/guide/topics/ui/notifiers/notifications.jd +++ b/docs/html/guide/topics/ui/notifiers/notifications.jd @@ -16,6 +16,7 @@ page.title=Notifications <li><a href="#Required">Required notification contents</a></li> <li><a href="#Optional">Optional notification contents and settings</a></li> <li><a href="#Actions">Notification actions</a></li> + <li><a href="#Priority">Notification priority</a></li> <li><a href="#SimpleNotification">Creating a simple notification</a></li> <li><a href="#ApplyStyle">Applying a big view style to a notification</a></li> <li><a href="#Compatibility">Handling compatibility</a></li> @@ -290,6 +291,26 @@ page.title=Notifications {@link android.support.v4.app.NotificationCompat.Builder}. </p> <!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Priority">Notification priority</h3> +<p> + If you wish, you can set the priority of a notification. The priority acts + as a hint to the device UI about how the notification should be displayed. + To set a notification's priority, call {@link + android.support.v4.app.NotificationCompat.Builder#setPriority(int) + NotificationCompat.Builder.setPriority()} and pass in one of the {@link + android.support.v4.app.NotificationCompat} priority constants. There are + five priority levels, ranging from {@link + android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) to {@link + android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); if not set, the + priority defaults to {@link + android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0). +</p> +<p> For information about setting an appropriate priority level, see "Correctly + set and manage notification priority" in the <a + href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design + guide. +</p> +<!-- ------------------------------------------------------------------------------------------ --> <h3 id="SimpleNotification">Creating a simple notification</h3> <p> The following snippet illustrates a simple notification that specifies an activity to open when diff --git a/docs/html/wear/images/notif_summary_framed.png b/docs/html/wear/images/notif_summary_framed.png Binary files differnew file mode 100644 index 0000000..17b1703 --- /dev/null +++ b/docs/html/wear/images/notif_summary_framed.png diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd index 7f955f6..a2d34ce 100644 --- a/docs/html/wear/notifications/stacks.jd +++ b/docs/html/wear/notifications/stacks.jd @@ -2,8 +2,8 @@ page.title=Stacking Notifications @jd:body -<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" /> -<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" /> +<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" /> +<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" /> <p>When creating notifications for a handheld device, you should always aggregate similar notifications into a single summary notification. For example, if your app creates notifications @@ -29,20 +29,44 @@ Wear</a>.</p> <p>To create a stack, call <a href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)"> -<code>setGroup()</code></a> for each notification you want in the stack, passing the same -group key. For example:</p> +<code>setGroup()</code></a> for each notification you want in the stack and specify a +group key. Then call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a> to send it to the wearable.</p> <pre style="clear:right"> final static String GROUP_KEY_EMAILS = "group_key_emails"; +// Build the notification and pass this builder to WearableNotifications.Builder NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext) - .setContentTitle("New mail from " + sender) - .setContentText(subject) + .setContentTitle("New mail from " + sender1) + .setContentText(subject1) .setSmallIcon(R.drawable.new_mail); -Notification notif = new WearableNotifications.Builder(builder) +Notification notif1 = new WearableNotifications.Builder(builder) .setGroup(GROUP_KEY_EMAILS) .build(); + +// Issue the notification +NotificationManagerCompat notificationManager = + NotificationManagerCompat.from(this); +notificationManager.notify(notificationId1, notif); +</pre> + +<p>Later on, when you create another notification, specify +the same group key. When you call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a>, this notification appears +in the same stack as the previous notification, instead of as a new card:</p> + +<pre style="clear:right"> +builder = new NotificationCompat.Builder(mContext) + .setContentTitle("New mail from " + sender2) + .setContentText(subject2) + .setSmallIcon(R.drawable.new_mail); + +// Use the same group as the previous notification +Notification notif2 = new WearableNotifications.Builder(builder) + .setGroup(GROUP_KEY_EMAILS) + .build(); + +notificationManager.notify(notificationId2, notif); </pre> <p>By default, notifications appear in the order in which you added them, with the most recent @@ -54,19 +78,55 @@ href="{@docRoot}reference/android/preview/support/wearable/notifications/Wearabl <h2 id="AddSummary">Add a Summary Notification</h2> +<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" /> + <p>It's important that you still provide a summary notification that appears on handheld devices. So in addition to adding each unique notification to the same stack group, also add a summary notification, but set its order position to be <a href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p> -<pre> -Notification summaryNotification = new WearableNotifications.Builder(builder) - .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY) - .build(); -</pre> +<p>This notification does not appear in your stack of notifications on the wearable, but +appears as the only notification on the handheld device.</p> -<p>This notification will not appear in your stack of notifications on the wearable, but -appears as the only notification on the handheld device. +<pre style="clear:right"> +Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_large_icon); + +builder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_small_icon) + .setLargeIcon(largeIcon); + +// Use the same group key and pass this builder to InboxStyle notification +WearableNotifications.Builder wearableBuilder = new WearableNotifications + .Builder(builder) + .setGroup(GROUP_KEY_EMAILS, + WearableNotifications.GROUP_ORDER_SUMMARY); + +// Build the final notification to show on the handset +Notification summaryNotification = new NotificationCompat.InboxStyle( + wearableBuilder.getCompatBuilder()) + .addLine("Alex Faaborg Check this out") + .addLine("Jeff Chang Launch Party") + .setBigContentTitle("2 new messages") + .setSummaryText("johndoe@gmail.com") + .build(); + +notificationManager.notify(notificationId3, summaryNotification); +</pre> +<p> +This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle}, +which gives you an easy way to create notifications for email or messaging apps. +You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat}, +or no style for the summary notification. +</p> + +<p class="note"><b>Tip:</b> +To style the text like in the example screenshot, see +<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling +with HTML markup</a> and +<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling +with Spannables</a>. +</p> </body> -</html> +</html>
\ No newline at end of file diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java index 99ea9b1..be86060 100644 --- a/graphics/java/android/graphics/CanvasProperty.java +++ b/graphics/java/android/graphics/CanvasProperty.java @@ -16,12 +16,15 @@ package android.graphics; +import com.android.internal.util.VirtualRefBasePtr; + /** * TODO: Make public? * @hide */ public final class CanvasProperty<T> { - private long mNativeContainer; + + private VirtualRefBasePtr mProperty; public static CanvasProperty<Float> createFloat(float initialValue) { return new CanvasProperty<Float>(nCreateFloat(initialValue)); @@ -32,25 +35,14 @@ public final class CanvasProperty<T> { } private CanvasProperty(long nativeContainer) { - mNativeContainer = nativeContainer; + mProperty = new VirtualRefBasePtr(nativeContainer); } /** @hide */ public long getNativeContainer() { - return mNativeContainer; - } - - @Override - protected void finalize() throws Throwable { - try { - nUnref(mNativeContainer); - mNativeContainer = 0; - } finally { - super.finalize(); - } + return mProperty.get(); } private static native long nCreateFloat(float initialValue); private static native long nCreatePaint(long initialValuePaintPtr); - private static native void nUnref(long ptr); } diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index 3f8c45c..0862cdd 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -18,6 +18,7 @@ package android.graphics; import java.lang.ref.WeakReference; +import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -132,14 +133,14 @@ public class SurfaceTexture { * Register a callback to be invoked when a new image frame becomes available to the * SurfaceTexture. * <p> - * This callback may be called on an arbitrary thread, so it is not + * The callback may be called on an arbitrary thread, so it is not * safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the * thread invoking the callback. * </p> * - * @param listener The listener to set. + * @param listener The listener to use, or null to remove the listener. */ - public void setOnFrameAvailableListener(OnFrameAvailableListener listener) { + public void setOnFrameAvailableListener(@Nullable OnFrameAvailableListener listener) { setOnFrameAvailableListener(listener, null); } @@ -147,17 +148,18 @@ public class SurfaceTexture { * Register a callback to be invoked when a new image frame becomes available to the * SurfaceTexture. * <p> - * If no handler is specified, then this callback may be called on an arbitrary thread, + * If a handler is specified, the callback will be invoked on that handler's thread. + * If no handler is specified, then the callback may be called on an arbitrary thread, * so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES * context to the thread invoking the callback. * </p> * - * @param listener The listener to set. + * @param listener The listener to use, or null to remove the listener. * @param handler The handler on which the listener should be invoked, or null * to use an arbitrary thread. */ - public void setOnFrameAvailableListener(final OnFrameAvailableListener listener, - Handler handler) { + public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener, + @Nullable Handler handler) { if (listener != null) { // Although we claim the thread is arbitrary, earlier implementation would // prefer to send the callback on the creating looper or the main looper diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java new file mode 100644 index 0000000..46e3401 --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -0,0 +1,524 @@ +/* + * 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.graphics.drawable; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.LongSparseLongArray; +import android.util.SparseIntArray; +import android.util.StateSet; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * Drawable containing a set of Drawable keyframes where the currently displayed + * keyframe is chosen based on the current state set. Animations between + * keyframes may optionally be defined using transition elements. + * <p> + * This drawable can be defined in an XML file with the <code> + * <animated-selector></code> element. Each keyframe Drawable is defined in a + * nested <code><item></code> element. Transitions are defined in a nested + * <code><transition></code> element. + * + * @attr ref android.R.styleable#DrawableStates_state_focused + * @attr ref android.R.styleable#DrawableStates_state_window_focused + * @attr ref android.R.styleable#DrawableStates_state_enabled + * @attr ref android.R.styleable#DrawableStates_state_checkable + * @attr ref android.R.styleable#DrawableStates_state_checked + * @attr ref android.R.styleable#DrawableStates_state_selected + * @attr ref android.R.styleable#DrawableStates_state_activated + * @attr ref android.R.styleable#DrawableStates_state_active + * @attr ref android.R.styleable#DrawableStates_state_single + * @attr ref android.R.styleable#DrawableStates_state_first + * @attr ref android.R.styleable#DrawableStates_state_middle + * @attr ref android.R.styleable#DrawableStates_state_last + * @attr ref android.R.styleable#DrawableStates_state_pressed + */ +public class AnimatedStateListDrawable extends StateListDrawable { + private static final String ELEMENT_TRANSITION = "transition"; + private static final String ELEMENT_ITEM = "item"; + + private AnimatedStateListState mState; + + /** The currently running animation, if any. */ + private ObjectAnimator mAnim; + + /** Index to be set after the animation ends. */ + private int mAnimToIndex = -1; + + /** Index away from which we are animating. */ + private int mAnimFromIndex = -1; + + private boolean mMutated; + + public AnimatedStateListDrawable() { + this(null, null); + } + + /** + * Add a new drawable to the set of keyframes. + * + * @param stateSet An array of resource IDs to associate with the keyframe + * @param drawable The drawable to show when in the specified state + * @param id The unique identifier for the keyframe + */ + public void addState(int[] stateSet, Drawable drawable, int id) { + if (drawable != null) { + mState.addStateSet(stateSet, drawable, id); + onStateChange(getState()); + } + } + + /** + * Adds a new transition between keyframes. + * + * @param fromId Unique identifier of the starting keyframe + * @param toId Unique identifier of the ending keyframe + * @param anim An AnimationDrawable to use as a transition + * @param reversible Whether the transition can be reversed + */ + public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) { + mState.addTransition(fromId, toId, anim, reversible); + } + + @Override + public boolean isStateful() { + return true; + } + + @Override + protected boolean onStateChange(int[] stateSet) { + final int keyframeIndex = mState.indexOfKeyframe(stateSet); + if (keyframeIndex == getCurrentIndex()) { + return false; + } + + if (selectTransition(keyframeIndex)) { + return true; + } + + if (selectDrawable(keyframeIndex)) { + return true; + } + + return super.onStateChange(stateSet); + } + + private boolean selectTransition(int toIndex) { + if (mAnim != null) { + if (toIndex == mAnimToIndex) { + // Already animating to that keyframe. + return true; + } else if (toIndex == mAnimFromIndex) { + // Reverse the current animation. + mAnim.reverse(); + mAnimFromIndex = mAnimToIndex; + mAnimToIndex = toIndex; + return true; + } + + // Changing animation, end the current animation. + mAnim.end(); + } + + final AnimatedStateListState state = mState; + final int fromIndex = getCurrentIndex(); + final int fromId = state.getKeyframeIdAt(fromIndex); + final int toId = state.getKeyframeIdAt(toIndex); + + if (toId == 0 || fromId == 0) { + // Missing a keyframe ID. + return false; + } + + final int transitionIndex = state.indexOfTransition(fromId, toId); + if (transitionIndex < 0 || !selectDrawable(transitionIndex)) { + // Couldn't select a transition. + return false; + } + + final Drawable d = getCurrent(); + if (!(d instanceof AnimationDrawable)) { + // Transition isn't an animation. + return false; + } + + final AnimationDrawable ad = (AnimationDrawable) d; + final boolean reversed = mState.isTransitionReversed(fromId, toId); + final int frameCount = ad.getNumberOfFrames(); + final int fromFrame = reversed ? frameCount - 1 : 0; + final int toFrame = reversed ? 0 : frameCount - 1; + + final FrameInterpolator interp = new FrameInterpolator(ad, reversed); + final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame); + anim.setAutoCancel(true); + anim.setDuration(interp.getTotalDuration()); + anim.addListener(mAnimListener); + anim.setInterpolator(interp); + anim.start(); + + mAnim = anim; + mAnimFromIndex = fromIndex; + mAnimToIndex = toIndex; + return true; + } + + @Override + public void jumpToCurrentState() { + super.jumpToCurrentState(); + + if (mAnim != null) { + mAnim.end(); + } + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + throws XmlPullParserException, IOException { + final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable); + + super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible); + + final StateListState stateListState = getStateListState(); + stateListState.setVariablePadding(a.getBoolean( + R.styleable.AnimatedStateListDrawable_variablePadding, false)); + stateListState.setConstantSize(a.getBoolean( + R.styleable.AnimatedStateListDrawable_constantSize, false)); + stateListState.setEnterFadeDuration(a.getInt( + R.styleable.AnimatedStateListDrawable_enterFadeDuration, 0)); + stateListState.setExitFadeDuration(a.getInt( + R.styleable.AnimatedStateListDrawable_exitFadeDuration, 0)); + + setDither(a.getBoolean(R.styleable.AnimatedStateListDrawable_dither, true)); + setAutoMirrored(a.getBoolean(R.styleable.AnimatedStateListDrawable_autoMirrored, false)); + + a.recycle(); + + int type; + + final int innerDepth = parser.getDepth() + 1; + int depth; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth) { + continue; + } + + if (parser.getName().equals(ELEMENT_ITEM)) { + parseItem(r, parser, attrs, theme); + } else if (parser.getName().equals(ELEMENT_TRANSITION)) { + parseTransition(r, parser, attrs, theme); + } + } + + onStateChange(getState()); + } + + private int parseTransition(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + throws XmlPullParserException, IOException { + int drawableRes = 0; + int fromId = 0; + int toId = 0; + boolean reversible = false; + + final int numAttrs = attrs.getAttributeCount(); + for (int i = 0; i < numAttrs; i++) { + final int stateResId = attrs.getAttributeNameResource(i); + switch (stateResId) { + case 0: + break; + case R.attr.fromId: + fromId = attrs.getAttributeResourceValue(i, 0); + break; + case R.attr.toId: + toId = attrs.getAttributeResourceValue(i, 0); + break; + case R.attr.drawable: + drawableRes = attrs.getAttributeResourceValue(i, 0); + break; + case R.attr.reversible: + reversible = attrs.getAttributeBooleanValue(i, false); + break; + } + } + + final Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + int type; + while ((type = parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme); + } + + final AnimationDrawable anim; + if (dr instanceof AnimationDrawable) { + anim = (AnimationDrawable) dr; + } else { + throw new XmlPullParserException(parser.getPositionDescription() + + ": <transition> tag requires a 'drawable' attribute or " + + "child tag defining a drawable of type <animation>"); + } + + return mState.addTransition(fromId, toId, anim, reversible); + } + + private int parseItem(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + throws XmlPullParserException, IOException { + int drawableRes = 0; + int keyframeId = 0; + + int j = 0; + final int numAttrs = attrs.getAttributeCount(); + int[] states = new int[numAttrs]; + for (int i = 0; i < numAttrs; i++) { + final int stateResId = attrs.getAttributeNameResource(i); + switch (stateResId) { + case 0: + break; + case R.attr.id: + keyframeId = attrs.getAttributeResourceValue(i, 0); + break; + case R.attr.drawable: + drawableRes = attrs.getAttributeResourceValue(i, 0); + break; + default: + final boolean hasState = attrs.getAttributeBooleanValue(i, false); + states[j++] = hasState ? stateResId : -stateResId; + } + } + states = StateSet.trimStateSet(states, j); + + final Drawable dr; + if (drawableRes != 0) { + dr = r.getDrawable(drawableRes); + } else { + int type; + while ((type = parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": <item> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); + } + dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme); + } + + return mState.addStateSet(states, dr, keyframeId); + } + + @Override + public Drawable mutate() { + if (!mMutated) { + final AnimatedStateListState newState = new AnimatedStateListState(mState, this, null); + setConstantState(newState); + mMutated = true; + } + + return this; + } + + private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anim) { + selectDrawable(mAnimToIndex); + + mAnimToIndex = -1; + mAnimFromIndex = -1; + mAnim = null; + } + }; + + static class AnimatedStateListState extends StateListState { + private static final int REVERSE_SHIFT = 32; + private static final int REVERSE_MASK = 0x1; + + final LongSparseLongArray mTransitions; + final SparseIntArray mStateIds; + + AnimatedStateListState(AnimatedStateListState orig, AnimatedStateListDrawable owner, + Resources res) { + super(orig, owner, res); + + if (orig != null) { + mTransitions = orig.mTransitions.clone(); + mStateIds = orig.mStateIds.clone(); + } else { + mTransitions = new LongSparseLongArray(); + mStateIds = new SparseIntArray(); + } + } + + int addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) { + final int pos = super.addChild(anim); + final long keyFromTo = generateTransitionKey(fromId, toId); + mTransitions.append(keyFromTo, pos); + + if (reversible) { + final long keyToFrom = generateTransitionKey(toId, fromId); + mTransitions.append(keyToFrom, pos | (1L << REVERSE_SHIFT)); + } + + return addChild(anim); + } + + int addStateSet(int[] stateSet, Drawable drawable, int id) { + final int index = super.addStateSet(stateSet, drawable); + mStateIds.put(index, id); + return index; + } + + int indexOfKeyframe(int[] stateSet) { + final int index = super.indexOfStateSet(stateSet); + if (index >= 0) { + return index; + } + + return super.indexOfStateSet(StateSet.WILD_CARD); + } + + int getKeyframeIdAt(int index) { + return index < 0 ? 0 : mStateIds.get(index, 0); + } + + int indexOfTransition(int fromId, int toId) { + final long keyFromTo = generateTransitionKey(fromId, toId); + return (int) mTransitions.get(keyFromTo, -1); + } + + boolean isTransitionReversed(int fromId, int toId) { + final long keyFromTo = generateTransitionKey(fromId, toId); + return (mTransitions.get(keyFromTo, -1) >> REVERSE_SHIFT & REVERSE_MASK) == 1; + } + + @Override + public Drawable newDrawable() { + return new AnimatedStateListDrawable(this, null); + } + + @Override + public Drawable newDrawable(Resources res) { + return new AnimatedStateListDrawable(this, res); + } + + private static long generateTransitionKey(int fromId, int toId) { + return (long) fromId << 32 | toId; + } + } + + void setConstantState(AnimatedStateListState state) { + super.setConstantState(state); + + mState = state; + } + + private AnimatedStateListDrawable(AnimatedStateListState state, Resources res) { + super(null); + + final AnimatedStateListState newState = new AnimatedStateListState(state, this, res); + setConstantState(newState); + onStateChange(getState()); + jumpToCurrentState(); + } + + /** + * Interpolates between frames with respect to their individual durations. + */ + private static class FrameInterpolator implements TimeInterpolator { + private int[] mFrameTimes; + private int mFrames; + private int mTotalDuration; + + public FrameInterpolator(AnimationDrawable d, boolean reversed) { + updateFrames(d, reversed); + } + + public int updateFrames(AnimationDrawable d, boolean reversed) { + final int N = d.getNumberOfFrames(); + mFrames = N; + + if (mFrameTimes == null || mFrameTimes.length < N) { + mFrameTimes = new int[N]; + } + + final int[] frameTimes = mFrameTimes; + int totalDuration = 0; + for (int i = 0; i < N; i++) { + final int duration = d.getDuration(reversed ? N - i - 1 : i); + frameTimes[i] = duration; + totalDuration += duration; + } + + mTotalDuration = totalDuration; + return totalDuration; + } + + public int getTotalDuration() { + return mTotalDuration; + } + + @Override + public float getInterpolation(float input) { + final int elapsed = (int) (input * mTotalDuration + 0.5f); + final int N = mFrames; + final int[] frameTimes = mFrameTimes; + + // Find the current frame and remaining time within that frame. + int remaining = elapsed; + int i = 0; + while (i < N && remaining >= frameTimes[i]) { + remaining -= frameTimes[i]; + i++; + } + + // Remaining time is relative of total duration. + final float frameElapsed; + if (i < N) { + frameElapsed = remaining / (float) mTotalDuration; + } else { + frameElapsed = 0; + } + + return i / (float) N + frameElapsed; + } + } +} + diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index 3f94e26..da4bc10 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -94,7 +94,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An boolean changed = super.setVisible(visible, restart); if (visible) { if (changed || restart) { - setFrame(0, true, true); + setFrame(0, true, mCurFrame >= 0); } } else { unscheduleSelf(this); diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index b9d5e19..b939636 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1039,6 +1039,8 @@ public abstract class Drawable { final String name = parser.getName(); if (name.equals("selector")) { drawable = new StateListDrawable(); + } else if (name.equals("animated-selector")) { + drawable = new AnimatedStateListDrawable(); } else if (name.equals("level-list")) { drawable = new LevelListDrawable(); } else if (name.equals("layer-list")) { diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 1f8b51d..08fc99d 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -359,6 +359,16 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawableContainerState.getOpacity(); } + /** @hide */ + public void setCurrentIndex(int index) { + selectDrawable(index); + } + + /** @hide */ + public int getCurrentIndex() { + return mCurIndex; + } + public boolean selectDrawable(int idx) { if (idx == mCurIndex) { return false; diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 271af2b..f22a063 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -55,8 +55,9 @@ import android.util.StateSet; * @attr ref android.R.styleable#DrawableStates_state_pressed */ public class StateListDrawable extends DrawableContainer { + private static final String TAG = StateListDrawable.class.getSimpleName(); + private static final boolean DEBUG = false; - private static final String TAG = "StateListDrawable"; /** * To be proper, we should have a getter for dither (and alpha, etc.) @@ -69,7 +70,8 @@ public class StateListDrawable extends DrawableContainer { * to improve the quality at negligible cost. */ private static final boolean DEFAULT_DITHER = true; - private final StateListState mStateListState; + + private StateListState mStateListState; private boolean mMutated; public StateListDrawable() { @@ -274,7 +276,7 @@ public class StateListDrawable extends DrawableContainer { mStateListState.setLayoutDirection(layoutDirection); } - static final class StateListState extends DrawableContainerState { + static class StateListState extends DrawableContainerState { int[][] mStateSets; StateListState(StateListState orig, StateListDrawable owner, Resources res) { @@ -293,7 +295,7 @@ public class StateListDrawable extends DrawableContainer { return pos; } - private int indexOfStateSet(int[] stateSet) { + int indexOfStateSet(int[] stateSet) { final int[][] stateSets = mStateSets; final int N = getChildCount(); for (int i = 0; i < N; i++) { @@ -323,11 +325,26 @@ public class StateListDrawable extends DrawableContainer { } } + void setConstantState(StateListState state) { + super.setConstantState(state); + + mStateListState = state; + } + private StateListDrawable(StateListState state, Resources res) { - StateListState as = new StateListState(state, this, res); - mStateListState = as; - setConstantState(as); + final StateListState newState = new StateListState(state, this, res); + setConstantState(newState); onStateChange(getState()); } + + /** + * This constructor exists so subclasses can avoid calling the default + * constructor and setting up a StateListDrawable-specific constant state. + */ + StateListDrawable(StateListState state) { + if (state != null) { + setConstantState(state); + } + } } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 098753b..6aad5fb 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2452,15 +2452,19 @@ String8 ResTable_config::toString() const { if (mcc != 0) { if (res.size() > 0) res.append("-"); - res.appendFormat("%dmcc", dtohs(mcc)); + res.appendFormat("mcc%d", dtohs(mcc)); } if (mnc != 0) { if (res.size() > 0) res.append("-"); - res.appendFormat("%dmnc", dtohs(mnc)); + res.appendFormat("mnc%d", dtohs(mnc)); } + char localeStr[RESTABLE_MAX_LOCALE_LEN]; getBcp47Locale(localeStr); - res.append(localeStr); + if (strlen(localeStr) > 0) { + if (res.size() > 0) res.append("-"); + res.append(localeStr); + } if ((screenLayout&MASK_LAYOUTDIR) != 0) { if (res.size() > 0) res.append("-"); @@ -2627,6 +2631,20 @@ String8 ResTable_config::toString() const { break; } } + if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_KEYSHIDDEN) { + case ResTable_config::KEYSHIDDEN_NO: + res.append("keysexposed"); + break; + case ResTable_config::KEYSHIDDEN_YES: + res.append("keyshidden"); + break; + case ResTable_config::KEYSHIDDEN_SOFT: + res.append("keyssoft"); + break; + } + } if (keyboard != KEYBOARD_ANY) { if (res.size() > 0) res.append("-"); switch (keyboard) { @@ -2644,17 +2662,18 @@ String8 ResTable_config::toString() const { break; } } - if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if ((inputFlags&MASK_NAVHIDDEN) != 0) { if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - res.append("keysexposed"); + switch (inputFlags&MASK_NAVHIDDEN) { + case ResTable_config::NAVHIDDEN_NO: + res.append("navexposed"); break; - case ResTable_config::KEYSHIDDEN_YES: - res.append("keyshidden"); + case ResTable_config::NAVHIDDEN_YES: + res.append("navhidden"); break; - case ResTable_config::KEYSHIDDEN_SOFT: - res.append("keyssoft"); + default: + res.appendFormat("inputFlagsNavHidden=%d", + dtohs(inputFlags&MASK_NAVHIDDEN)); break; } } @@ -2678,21 +2697,6 @@ String8 ResTable_config::toString() const { break; } } - if ((inputFlags&MASK_NAVHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - res.append("navsexposed"); - break; - case ResTable_config::NAVHIDDEN_YES: - res.append("navhidden"); - break; - default: - res.appendFormat("inputFlagsNavHidden=%d", - dtohs(inputFlags&MASK_NAVHIDDEN)); - break; - } - } if (screenSize != 0) { if (res.size() > 0) res.append("-"); res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); @@ -5503,7 +5507,25 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (package == NULL) { return (mError=NO_MEMORY); } - + + if (idmap_id == 0) { + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } + + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } + } + if (id == 0) { // This is a library so assign an ID id = mNextPackageId++; @@ -5521,21 +5543,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=NO_MEMORY); } - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - //printf("Adding new package id %d at index %d\n", id, idx); err = mPackageGroups.add(group); if (err < NO_ERROR) { diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index eb0cac8..2cadf09 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -6,6 +6,7 @@ include $(CLEAR_VARS) ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SRC_FILES := \ utils/Blur.cpp \ + utils/GLUtils.cpp \ utils/SortedListImpl.cpp \ thread/TaskManager.cpp \ font/CacheTexture.cpp \ @@ -53,7 +54,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) TextureCache.cpp \ TextDropShadowCache.cpp - # RenderThread stuff +# RenderThread stuff LOCAL_SRC_FILES += \ renderthread/CanvasContext.cpp \ renderthread/DrawFrameTask.cpp \ diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 0b074cc..86fc7c3 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -17,13 +17,13 @@ #define ANIMATOR_H #include <cutils/compiler.h> +#include <utils/RefBase.h> #include <utils/StrongPointer.h> #include "CanvasProperty.h" #include "Interpolator.h" #include "TreeInfo.h" #include "utils/Macros.h" -#include "utils/VirtualLightRefBase.h" namespace android { namespace uirenderer { diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h index 2e1d176..6074394 100644 --- a/libs/hwui/CanvasProperty.h +++ b/libs/hwui/CanvasProperty.h @@ -16,8 +16,9 @@ #ifndef CANVASPROPERTY_H #define CANVASPROPERTY_H +#include <utils/RefBase.h> + #include "utils/Macros.h" -#include "utils/VirtualLightRefBase.h" #include <SkPaint.h> diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index eaeb772..b2ead5b 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -41,7 +41,6 @@ #include "Matrix.h" #include "DeferredDisplayList.h" #include "RenderProperties.h" -#include "utils/VirtualLightRefBase.h" class SkBitmap; class SkPaint; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index c2ce6ed..2391e80 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -58,7 +58,7 @@ DisplayListData* DisplayListRenderer::finishRecording() { void DisplayListRenderer::setViewport(int width, int height) { // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used - mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); + mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); initializeViewport(width, height); } diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index f06106b..2268386 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -482,8 +482,8 @@ void Matrix4::decomposeScale(float& sx, float& sy) const { sy = copysignf(sqrtf(len), data[mat4::kScaleY]); } -void Matrix4::dump() const { - ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType()); +void Matrix4::dump(const char* label) const { + ALOGD("%s[simple=%d, type=0x%x", label ? label : "Matrix4", isSimple(), getType()); ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 26cb05f..e33a001 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -209,7 +209,7 @@ public: void decomposeScale(float& sx, float& sy) const; - void dump() const; + void dump(const char* label = NULL) const; static const Matrix4& identity(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 87b07b3..20b038d 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -37,6 +37,7 @@ #include "PathTessellator.h" #include "Properties.h" #include "ShadowTessellator.h" +#include "utils/GLUtils.h" #include "Vector.h" #include "VertexBuffer.h" @@ -170,7 +171,7 @@ void OpenGLRenderer::setViewport(int width, int height) { } void OpenGLRenderer::initViewport(int width, int height) { - mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); + mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); initializeViewport(width, height); } @@ -296,24 +297,7 @@ void OpenGLRenderer::finish() { if (!suppressErrorChecks()) { #if DEBUG_OPENGL - GLenum status = GL_NO_ERROR; - while ((status = glGetError()) != GL_NO_ERROR) { - ALOGD("GL error from OpenGLRenderer: 0x%x", status); - switch (status) { - case GL_INVALID_ENUM: - ALOGE(" GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ALOGE(" GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ALOGE(" GL_INVALID_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ALOGE(" Out of memory!"); - break; - } - } + GLUtils::dumpGLErrors(); #endif #if DEBUG_MEMORY_USAGE @@ -644,7 +628,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& if (restoreOrtho) { const Rect& r = restored.viewport; glViewport(r.left, r.top, r.right, r.bottom); - mViewProjMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? + mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? } if (restoreClip) { @@ -870,7 +854,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mViewProjMatrix); + mSnapshot->orthoMatrix.load(mProjectionMatrix); endTiling(); debugOverdraw(false, false); @@ -900,8 +884,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - // TODO: determine best way to support 3d drawing within HW layers - mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); return true; } @@ -1705,17 +1688,17 @@ void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, float left, float top, float right, float bottom, bool ignoreTransform) { - mModelView.loadTranslate(left, top, 0.0f); + mModelViewMatrix.loadTranslate(left, top, 0.0f); if (mode == kModelViewMode_TranslateAndScale) { - mModelView.scale(right - left, bottom - top, 1.0f); + mModelViewMatrix.scale(right - left, bottom - top, 1.0f); } bool dirty = right - left > 0.0f && bottom - top > 0.0f; if (!ignoreTransform) { - mCaches.currentProgram->set(mViewProjMatrix, mModelView, *currentTransform(), offset); + mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform()); } else { - mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset); + mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); } } @@ -1740,11 +1723,11 @@ void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) { // compensate. mat4 modelViewWithoutTransform; modelViewWithoutTransform.loadInverse(*currentTransform()); - modelViewWithoutTransform.multiply(mModelView); - mModelView.load(modelViewWithoutTransform); + modelViewWithoutTransform.multiply(mModelViewMatrix); + mModelViewMatrix.load(modelViewWithoutTransform); } mDrawModifiers.mShader->setupProgram(mCaches.currentProgram, - mModelView, *mSnapshot, &mTextureUnit); + mModelViewMatrix, *mSnapshot, &mTextureUnit); } } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 1d46945..4f7f01e 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -930,8 +930,8 @@ private: */ Texture* getTexture(const SkBitmap* bitmap); - // Matrix used for view/projection in shaders - mat4 mViewProjMatrix; + // Ortho matrix used for projection in shaders + mat4 mProjectionMatrix; /** * Model-view matrix used to position/size objects @@ -939,15 +939,15 @@ private: * Stores operation-local modifications to the draw matrix that aren't incorporated into the * currentTransform(). * - * If generated with kModelViewMode_Translate, the mModelView will reflect an x/y offset, + * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset, * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale, - * mModelView will reflect a translation and scale, e.g. the translation and scale required to - * make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height of a - * bitmap. + * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale + * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height + * of a bitmap. * * Used as input to SkiaShader transformation. */ - mat4 mModelView; + mat4 mModelViewMatrix; // State used to define the clipping region Rect mTilingClip; diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 92964a8..f38d8b7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -175,6 +175,10 @@ public: bottom += dy; } + void inset(float delta) { + outset(-delta); + } + void outset(float delta) { left -= delta; top -= delta; @@ -230,8 +234,8 @@ public: bottom = ceilf(bottom); } - void dump() const { - ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); + void dump(const char* label) const { + ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } private: diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 159903c..bc62ee1 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -45,7 +45,6 @@ #include "DisplayList.h" #include "RenderProperties.h" #include "TreeInfo.h" -#include "utils/VirtualLightRefBase.h" class SkBitmap; class SkPaint; diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d26ee38..6bfa203 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -27,9 +27,15 @@ namespace uirenderer { // Constructors /////////////////////////////////////////////////////////////////////////////// -Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), - invisible(false), empty(false), alpha(1.0f) { - +Snapshot::Snapshot() + : flags(0) + , previous(NULL) + , layer(NULL) + , fbo(0) + , invisible(false) + , empty(false) + , height(0) + , alpha(1.0f) { transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; @@ -40,10 +46,16 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), * Copies the specified snapshot/ The specified snapshot is stored as * the previous snapshot. */ -Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(s->layer), fbo(s->fbo), - invisible(s->invisible), empty(false), - viewport(s->viewport), height(s->height), alpha(s->alpha) { +Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) + : flags(0) + , previous(s) + , layer(s->layer) + , fbo(s->fbo) + , invisible(s->invisible) + , empty(false) + , viewport(s->viewport) + , height(s->height) + , alpha(s->alpha) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index d22cb8a..08e9a1a 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -214,18 +214,28 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* int dstY = y + glyph->mBitmapTop; CacheTexture* cacheTexture = glyph->mCacheTexture; + PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); + uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat()); uint32_t cacheWidth = cacheTexture->getWidth(); - uint32_t startY = glyph->mStartY * cacheWidth; - uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth); + uint32_t srcStride = formatSize * cacheWidth; + uint32_t startY = glyph->mStartY * srcStride; + uint32_t endY = startY + (glyph->mBitmapHeight * srcStride); - PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); const uint8_t* cacheBuffer = pixelBuffer->map(); for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY; - cacheY += cacheWidth, bitmapY += bitmapWidth) { - memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); + cacheY += srcStride, bitmapY += bitmapWidth) { + + if (formatSize == 1) { + memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); + } else { + for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) { + bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize]; + } + } } + } void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp new file mode 100644 index 0000000..9b298ca --- /dev/null +++ b/libs/hwui/utils/GLUtils.cpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/Log.h> + +#include "GLUtils.h" + +namespace android { +namespace uirenderer { + +void GLUtils::dumpGLErrors() { + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + switch (status) { + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + break; + default: + ALOGE("GL error: 0x%x", status); + } + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/GLUtils.h index b545aab..890e374 100644 --- a/libs/hwui/utils/VirtualLightRefBase.h +++ b/libs/hwui/utils/GLUtils.h @@ -13,22 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef VIRTUALLIGHTREFBASE_H -#define VIRTUALLIGHTREFBASE_H - -#include <utils/RefBase.h> +#ifndef GLUTILS_H +#define GLUTILS_H namespace android { namespace uirenderer { -// This is a wrapper around LightRefBase that simply enforces a virtual -// destructor to eliminate the template requirement of LightRefBase -class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> { +class GLUtils { +private: public: - virtual ~VirtualLightRefBase() {} -}; + /** + * Print out any GL errors with ALOGE + */ + static void dumpGLErrors(); + +}; // class GLUtils } /* namespace uirenderer */ } /* namespace android */ -#endif /* VIRTUALLIGHTREFBASE_H */ +#endif /* GLUTILS_H */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 7deabe9..8ba44dc 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -38,4 +38,4 @@ public: } /* namespace uirenderer */ } /* namespace android */ -#endif /* RENDERNODE_H */ +#endif /* MATHUTILS_H */ diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java new file mode 100644 index 0000000..bb23a36 --- /dev/null +++ b/media/java/android/media/AudioAttributes.java @@ -0,0 +1,444 @@ +/* + * 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.annotation.IntDef; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A class to encapsulate a collection of attributes describing information about an audio + * player or recorder. + */ +public final class AudioAttributes { + private final static String TAG = "AudioAttributes"; + + /** + * Content type value to use when the content type is unknown, or other than the ones defined. + */ + public final static int CONTENT_TYPE_UNKNOWN = 0; + /** + * Content type value to use when the content type is speech. + */ + public final static int CONTENT_TYPE_SPEECH = 1; + /** + * Content type value to use when the content type is music. + */ + public final static int CONTENT_TYPE_MUSIC = 2; + /** + * Content type value to use when the content type is a soundtrack, typically accompanying + * a movie or TV program. + */ + public final static int CONTENT_TYPE_MOVIE = 3; + /** + * Content type value to use when the content type is a sound used to accompany a user + * action, such as a beep or sound effect expressing a key click, or event, such as the + * type of a sound for a bonus being received in a game. These sounds are mostly synthesized + * or short Foley sounds. + */ + public final static int CONTENT_TYPE_SONIFICATION = 4; + + /** + * Usage value to use when the usage is unknown. + */ + public final static int USAGE_UNKNOWN = 0; + /** + * Usage value to use when the usage is media, such as music, or movie + * soundtracks. + */ + public final static int USAGE_MEDIA = 1; + /** + * Usage value to use when the usage is voice communications, such as telephony + * or VoIP. + */ + public final static int USAGE_VOICE_COMMUNICATION = 2; + /** + * Usage value to use when the usage is in-call signalling, such as with + * a "busy" beep, or DTMF tones. + */ + public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; + /** + * Usage value to use when the usage is an alarm (e.g. wake-up alarm). + */ + public final static int USAGE_ALARM = 4; + /** + * Usage value to use when the usage is notification. See other + * notification usages for more specialized uses. + */ + public final static int USAGE_NOTIFICATION = 5; + /** + * Usage value to use when the usage is telephony ringtone. + */ + public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; + /** + * Usage value to use when the usage is a request to enter/end a + * communication, such as a VoIP communication or video-conference. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; + /** + * Usage value to use when the usage is notification for an "instant" + * communication such as a chat, or SMS. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; + /** + * Usage value to use when the usage is notification for a + * non-immediate type of communication such as e-mail. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; + /** + * Usage value to use when the usage is to attract the user's attention, + * such as a reminder or low battery warning. + */ + public final static int USAGE_NOTIFICATION_EVENT = 10; + /** + * Usage value to use when the usage is for accessibility, such as with + * a screen reader. + */ + public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11; + /** + * Usage value to use when the usage is driving or navigation directions. + */ + public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; + /** + * Usage value to use when the usage is sonification, such as with user + * interface sounds. + */ + public final static int USAGE_ASSISTANCE_SONIFICATION = 13; + /** + * Usage value to use when the usage is for game audio. + */ + public final static int USAGE_GAME = 14; + + /** + * Flag defining a behavior where the audibility of the sound will be ensured by the system. + */ + public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0; + /** + * @hide + * Flag defining a behavior where the playback of the sound is ensured without + * degradation only when going to a secure sink. + */ + // FIXME not guaranteed yet + // TODO add OR to getFlags() when supported and in public API + public final static int FLAG_SECURE = 0x1 << 1; + /** + * @hide + * Flag to enable when the stream is associated with SCO usage. + * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO + */ + public final static int FLAG_SCO = 0x1 << 2; + + + private int mUsage = USAGE_UNKNOWN; + private int mContentType = CONTENT_TYPE_UNKNOWN; + private int mFlags = 0x0; + private HashSet<String> mTags; + + private AudioAttributes() { + } + + /** + * Return the content type. + * @return one of the values that can be set in {@link Builder#setContentType(int)} + */ + public int getContentType() { + return mContentType; + } + + /** + * Return the usage. + * @return one of the values that can be set in {@link Builder#setUsage(int)} + */ + public int getUsage() { + return mUsage; + } + + /** + * Return the flags. + * @return a combined mask of all flags + */ + public int getFlags() { + // only return the flags that are public + return (mFlags & (FLAG_AUDIBILITY_ENFORCED)); + } + + /** + * @hide + * Return all the flags, even the non-public ones. + * Internal use only + * @return a combined mask of all flags + */ + public int getAllFlags() { + return mFlags; + } + + /** + * Return the set of tags. + * @return a read-only set of all tags stored as strings. + */ + public Set<String> getTags() { + return Collections.unmodifiableSet(mTags); + } + + /** + * Builder class for {@link AudioAttributes} objects. + */ + public static class Builder { + private int mUsage = USAGE_UNKNOWN; + private int mContentType = CONTENT_TYPE_UNKNOWN; + private int mFlags = 0x0; + private HashSet<String> mTags = new HashSet<String>(); + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + } + + /** + * Constructs a new Builder from a given AudioAttributes + * @param aa the AudioAttributes object whose data will be reused in the new Builder. + */ + @SuppressWarnings("unchecked") // for cloning of mTags + public Builder(AudioAttributes aa) { + mUsage = aa.mUsage; + mContentType = aa.mContentType; + mFlags = aa.mFlags; + mTags = (HashSet<String>) aa.mTags.clone(); + } + + /** + * Combines all of the attributes that have been set and return a new + * {@link AudioAttributes} object. + * @return a new {@link AudioAttributes} object + */ + @SuppressWarnings("unchecked") // for cloning of mTags + public AudioAttributes build() { + AudioAttributes aa = new AudioAttributes(); + aa.mContentType = mContentType; + aa.mUsage = mUsage; + aa.mFlags = mFlags; + aa.mTags = (HashSet<String>) mTags.clone(); + return aa; + } + + /** + * Sets the attribute describing what is the intended use of the the audio signal, + * such as alarm or ringtone. + * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN}, + * {@link AudioAttributes#USAGE_MEDIA}, + * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION}, + * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING}, + * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, + * {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, + * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT}, + * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, + * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, + * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION}, + * {@link AudioAttributes#USAGE_GAME}. + * @return the same Builder instance. + */ + public Builder setUsage(@AttributeUsage int usage) { + switch (usage) { + case USAGE_UNKNOWN: + case USAGE_MEDIA: + case USAGE_VOICE_COMMUNICATION: + case USAGE_VOICE_COMMUNICATION_SIGNALLING: + case USAGE_ALARM: + case USAGE_NOTIFICATION: + case USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case USAGE_NOTIFICATION_EVENT: + case USAGE_ASSISTANCE_ACCESSIBILITY: + case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case USAGE_ASSISTANCE_SONIFICATION: + case USAGE_GAME: + mUsage = usage; + break; + default: + mUsage = USAGE_UNKNOWN; + } + return this; + } + + /** + * Sets the attribute describing the content type of the audio signal, such as speech, + * or music. + * @param contentType the content type values, one of + * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, + * {@link AudioAttributes#CONTENT_TYPE_MUSIC}, + * {@link AudioAttributes#CONTENT_TYPE_SONIFICATION}, + * {@link AudioAttributes#CONTENT_TYPE_SPEECH}, + * {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}. + * @return the same Builder instance. + */ + public Builder setContentType(@AttributeContentType int contentType) { + switch (contentType) { + case CONTENT_TYPE_UNKNOWN: + case CONTENT_TYPE_MOVIE: + case CONTENT_TYPE_MUSIC: + case CONTENT_TYPE_SONIFICATION: + case CONTENT_TYPE_SPEECH: + mContentType = contentType; + break; + default: + mUsage = CONTENT_TYPE_UNKNOWN; + } + return this; + } + + /** + * Sets the combination of flags. + * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag. + * @return the same Builder instance. + */ + public Builder setFlags(int flags) { + flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO + | AudioAttributes.FLAG_SECURE); + mFlags |= flags; + return this; + } + + /** + * Add a custom tag stored as a string + * @param tag + * @return the same Builder instance. + */ + public Builder addTag(String tag) { + mTags.add(tag); + return this; + } + + /** + * Adds attributes inferred from the legacy stream types. + * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL}, + * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING}, + * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM}, + * or {@link AudioManager#STREAM_NOTIFICATION}. + * @return the same Builder instance. + */ + public Builder setLegacyStreamType(int streamType) { + return setInternalLegacyStreamType(streamType); + } + + /** + * @hide + * For internal framework use only, enables building from hidden stream types. + * @param streamType + * @return the same Builder instance. + */ + public Builder setInternalLegacyStreamType(int streamType) { + switch(streamType) { + case AudioSystem.STREAM_VOICE_CALL: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_VOICE_COMMUNICATION; + break; + case AudioSystem.STREAM_SYSTEM_ENFORCED: + mFlags |= FLAG_AUDIBILITY_ENFORCED; + // intended fall through, attributes in common with STREAM_SYSTEM + case AudioSystem.STREAM_SYSTEM: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_ASSISTANCE_SONIFICATION; + break; + case AudioSystem.STREAM_RING: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE; + break; + case AudioSystem.STREAM_MUSIC: + mContentType = CONTENT_TYPE_MUSIC; + mUsage = USAGE_MEDIA; + break; + case AudioSystem.STREAM_ALARM: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_ALARM; + break; + case AudioSystem.STREAM_NOTIFICATION: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_NOTIFICATION; + break; + case AudioSystem.STREAM_BLUETOOTH_SCO: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_VOICE_COMMUNICATION; + mFlags |= FLAG_SCO; + break; + case AudioSystem.STREAM_DTMF: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING; + break; + case AudioSystem.STREAM_TTS: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_ASSISTANCE_ACCESSIBILITY; + break; + default: + Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes"); + } + return this; + } + }; + + /** @hide */ + @Override + public String toString () { + return new String("AudioAttributes:" + + " usage=" + mUsage + + " content=" + mContentType + + " flags=0x" + Integer.toHexString(mFlags) + + " tags=" + mTags); + } + + /** @hide */ + @IntDef({ + USAGE_UNKNOWN, + USAGE_MEDIA, + USAGE_VOICE_COMMUNICATION, + USAGE_VOICE_COMMUNICATION_SIGNALLING, + USAGE_ALARM, + USAGE_NOTIFICATION, + USAGE_NOTIFICATION_TELEPHONY_RINGTONE, + USAGE_NOTIFICATION_COMMUNICATION_REQUEST, + USAGE_NOTIFICATION_COMMUNICATION_INSTANT, + USAGE_NOTIFICATION_COMMUNICATION_DELAYED, + USAGE_NOTIFICATION_EVENT, + USAGE_ASSISTANCE_ACCESSIBILITY, + USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, + USAGE_ASSISTANCE_SONIFICATION, + USAGE_GAME + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeUsage {} + + /** @hide */ + @IntDef({ + CONTENT_TYPE_UNKNOWN, + CONTENT_TYPE_SPEECH, + CONTENT_TYPE_MUSIC, + CONTENT_TYPE_MOVIE, + CONTENT_TYPE_SONIFICATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeContentType {} +} diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 6b2a247..57274ee 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -37,7 +37,7 @@ public class AudioFormat { public static final int ENCODING_PCM_16BIT = 2; /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */ public static final int ENCODING_PCM_8BIT = 3; - /** @hide Candidate for public API */ + /** Audio data format: single-precision floating-point per sample */ public static final int ENCODING_PCM_FLOAT = 4; /** Invalid audio channel configuration */ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 0c8a823..724022b 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -149,6 +149,7 @@ public class AudioService extends IAudioService.Stub { private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 18; private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 19; private static final int MSG_UNLOAD_SOUND_EFFECTS = 20; + private static final int MSG_SYSTEM_READY = 21; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -370,7 +371,7 @@ public class AudioService extends IAudioService.Stub { private int mScoConnectionState; // true if boot sequence has been completed - private boolean mBootCompleted; + private boolean mSystemReady; // listener for SoundPool sample load completion indication private SoundPoolCallback mSoundPoolCallBack; // thread for SoundPool listener @@ -525,7 +526,6 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_DOCK_EVENT); intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG); intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG); - intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); intentFilter.addAction(Intent.ACTION_SCREEN_ON); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); @@ -559,6 +559,43 @@ public class AudioService extends IAudioService.Stub { } + public void systemReady() { + sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE, + 0, 0, null, 0); + } + + public void onSystemReady() { + mSystemReady = true; + sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, + 0, 0, null, 0); + + mKeyguardManager = + (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; + resetBluetoothSco(); + getBluetoothHeadset(); + //FIXME: this is to maintain compatibility with deprecated intent + // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. + Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); + newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + sendStickyBroadcastToAll(newIntent); + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, + BluetoothProfile.A2DP); + } + + sendMsg(mAudioHandler, + MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED, + SENDMSG_REPLACE, + 0, + 0, + null, + SAFE_VOLUME_CONFIGURE_TIMEOUT_MS); + } + private void createAudioSystemThread() { mAudioSystemThread = new AudioSystemThread(); mAudioSystemThread.start(); @@ -1996,7 +2033,7 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#startBluetoothSco() */ public void startBluetoothSco(IBinder cb, int targetSdkVersion){ if (!checkAudioSettingsPermission("startBluetoothSco()") || - !mBootCompleted) { + !mSystemReady) { return; } ScoClient client = getScoClient(cb, true); @@ -2013,7 +2050,7 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#stopBluetoothSco() */ public void stopBluetoothSco(IBinder cb){ if (!checkAudioSettingsPermission("stopBluetoothSco()") || - !mBootCompleted) { + !mSystemReady) { return; } ScoClient client = getScoClient(cb, false); @@ -3277,7 +3314,7 @@ public class AudioService extends IAudioService.Stub { int status; synchronized (mSoundEffectsLock) { - if (!mBootCompleted) { + if (!mSystemReady) { Log.w(TAG, "onLoadSoundEffects() called before boot complete"); return false; } @@ -3700,6 +3737,10 @@ public class AudioService extends IAudioService.Stub { case MSG_BROADCAST_BT_CONNECTION_STATE: onBroadcastScoConnectionState(msg.arg1); break; + + case MSG_SYSTEM_READY: + onSystemReady(); + break; } } } @@ -4169,36 +4210,6 @@ public class AudioService extends IAudioService.Stub { newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); sendStickyBroadcastToAll(newIntent); } - } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { - mBootCompleted = true; - sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, - 0, 0, null, 0); - - mKeyguardManager = - (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; - resetBluetoothSco(); - getBluetoothHeadset(); - //FIXME: this is to maintain compatibility with deprecated intent - // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. - Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); - newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - sendStickyBroadcastToAll(newIntent); - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, - BluetoothProfile.A2DP); - } - - sendMsg(mAudioHandler, - MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED, - SENDMSG_REPLACE, - 0, - 0, - null, - SAFE_VOLUME_CONFIGURE_TIMEOUT_MS); } else if (action.equals(Intent.ACTION_SCREEN_ON)) { AudioSystem.setParameters("screen_state=on"); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 007eb40..1a64cff 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -42,7 +42,8 @@ import com.android.internal.app.IAppOpsService; * The AudioTrack class manages and plays a single audio resource for Java applications. * It allows streaming of PCM audio buffers to the audio sink for playback. This is * achieved by "pushing" the data to the AudioTrack object using one of the - * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods. + * {@link #write(byte[], int, int)}, {@link #write(short[], int, int)}, + * and {@link #write(float[], int, int, int)} methods. * * <p>An AudioTrack instance can operate under two modes: static or streaming.<br> * In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using @@ -244,6 +245,7 @@ public class AudioTrack * The encoding of the audio samples. * @see AudioFormat#ENCODING_PCM_8BIT * @see AudioFormat#ENCODING_PCM_16BIT + * @see AudioFormat#ENCODING_PCM_FLOAT */ private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; /** @@ -285,8 +287,9 @@ public class AudioTrack * See {@link AudioFormat#CHANNEL_OUT_MONO} and * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat#ENCODING_PCM_16BIT} and - * {@link AudioFormat#ENCODING_PCM_8BIT} + * See {@link AudioFormat#ENCODING_PCM_16BIT}, + * {@link AudioFormat#ENCODING_PCM_8BIT}, + * and {@link AudioFormat#ENCODING_PCM_FLOAT}. * @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is * read from for playback. * If track's creation mode is {@link #MODE_STREAM}, you can write data into @@ -329,7 +332,8 @@ public class AudioTrack * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and - * {@link AudioFormat#ENCODING_PCM_8BIT} + * {@link AudioFormat#ENCODING_PCM_8BIT}, + * and {@link AudioFormat#ENCODING_PCM_FLOAT}. * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read * from for playback. If using the AudioTrack in streaming mode, you can write data into * this buffer in smaller chunks than this size. If using the AudioTrack in static mode, @@ -459,11 +463,14 @@ public class AudioTrack break; case AudioFormat.ENCODING_PCM_16BIT: case AudioFormat.ENCODING_PCM_8BIT: + case AudioFormat.ENCODING_PCM_FLOAT: mAudioFormat = audioFormat; break; default: throw new IllegalArgumentException("Unsupported sample encoding." - + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."); + + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT" + + " or ENCODING_PCM_FLOAT" + + "."); } //-------------- @@ -723,7 +730,8 @@ public class AudioTrack * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and - * {@link AudioFormat#ENCODING_PCM_8BIT} + * {@link AudioFormat#ENCODING_PCM_8BIT}, + * and {@link AudioFormat#ENCODING_PCM_FLOAT}. * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed, * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. @@ -750,7 +758,8 @@ public class AudioTrack } if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) - && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { + && (audioFormat != AudioFormat.ENCODING_PCM_8BIT) + && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } @@ -1150,7 +1159,7 @@ public class AudioTrack public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { - if (mState == STATE_UNINITIALIZED) { + if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } @@ -1188,13 +1197,13 @@ public class AudioTrack * starts. * @param sizeInShorts the number of shorts to read in audioData after the offset. * @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION} - * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if - * the parameters don't resolve to valid data and indexes. + * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if + * the parameters don't resolve to valid data and indexes. */ public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { - if (mState == STATE_UNINITIALIZED) { + if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } @@ -1220,6 +1229,79 @@ public class AudioTrack /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). + * In static buffer mode, copies the data to the buffer starting at offset 0, + * and the write mode is ignored. + * In streaming mode, the blocking behavior will depend on the write mode. + * <p> + * Note that the actual playback of this data might occur after this function + * returns. This function is thread safe with respect to {@link #stop} calls, + * in which case all of the specified data might not be written to the audio sink. + * <p> + * @param audioData the array that holds the data to play. + * The implementation does not clip for sample values within the nominal range + * [-1.0f, 1.0f], provided that all gains in the audio pipeline are + * less than or equal to unity (1.0f), and in the absence of post-processing effects + * that could add energy, such as reverb. For the convenience of applications + * that compute samples using filters with non-unity gain, + * sample values +3 dB beyond the nominal range are permitted. + * However such values may eventually be limited or clipped, depending on various gains + * and later processing in the audio path. Therefore applications are encouraged + * to provide samples values within the nominal range. + * @param offsetInFloats the offset, expressed as a number of floats, + * in audioData where the data to play starts. + * @param sizeInFloats the number of floats to read in audioData after the offset. + * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no + * effect in static mode. + * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written + * to the audio sink. + * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after + * queuing as much audio data for playback as possible without blocking. + * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION} + * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if + * the parameters don't resolve to valid data and indexes. + */ + public int write(float[] audioData, int offsetInFloats, int sizeInFloats, + @WriteMode int writeMode) { + + if (mState == STATE_UNINITIALIZED) { + Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); + return ERROR_INVALID_OPERATION; + } + + if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) { + Log.e(TAG, "AudioTrack.write(float[] ...) requires format ENCODING_PCM_FLOAT"); + return ERROR_INVALID_OPERATION; + } + + if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { + Log.e(TAG, "AudioTrack.write() called with invalid blocking mode"); + return ERROR_BAD_VALUE; + } + + if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0) + || (offsetInFloats + sizeInFloats < 0) // detect integer overflow + || (offsetInFloats + sizeInFloats > audioData.length)) { + Log.e(TAG, "AudioTrack.write() called with invalid array, offset, or size"); + return ERROR_BAD_VALUE; + } + + int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat, + writeMode == WRITE_BLOCKING); + + if ((mDataLoadMode == MODE_STATIC) + && (mState == STATE_NO_STATIC_DATA) + && (ret > 0)) { + // benign race with respect to other APIs that read mState + mState = STATE_INITIALIZED; + } + + return ret; + } + + + /** + * Writes the audio data to the audio sink for playback (streaming mode), + * or copies audio data for later playback (static buffer mode). * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write * mode is ignored. * In streaming mode, the blocking behavior will depend on the write mode. @@ -1247,6 +1329,11 @@ public class AudioTrack return ERROR_INVALID_OPERATION; } + if (mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { + Log.e(TAG, "AudioTrack.write(ByteBuffer ...) not yet supported for ENCODING_PCM_FLOAT"); + return ERROR_INVALID_OPERATION; + } + if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { Log.e(TAG, "AudioTrack.write() called with invalid blocking mode"); return ERROR_BAD_VALUE; @@ -1487,6 +1574,10 @@ public class AudioTrack private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); + private native final int native_write_float(float[] audioData, + int offsetInFloats, int sizeInFloats, int format, + boolean isBlocking); + private native final int native_write_native_bytes(Object audioData, int positionInBytes, int sizeInBytes, int format, boolean blocking); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 115786c..34c5520 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -585,11 +585,63 @@ final public class MediaCodec { * the codec. If you previously specified a surface when configuring this * video decoder you can optionally render the buffer. * @param index The index of a client-owned output buffer previously returned - * in a call to {@link #dequeueOutputBuffer}. + * from a call to {@link #dequeueOutputBuffer}. * @param render If a valid surface was specified when configuring the codec, * passing true renders this output buffer to the surface. */ - public native final void releaseOutputBuffer(int index, boolean render); + public final void releaseOutputBuffer(int index, boolean render) { + releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */); + } + + /** + * If you are done with a buffer, use this call to update its surface timestamp + * and return it to the codec to render it on the output surface. If you + * have not specified an output surface when configuring this video codec, + * this call will simply return the buffer to the codec.<p> + * + * The timestamp may have special meaning depending on the destination surface. + * + * <table> + * <tr><th>SurfaceView specifics</th></tr> + * <tr><td> + * If you render your buffer on a {@link android.view.SurfaceView}, + * you can use the timestamp to render the buffer at a specific time (at the + * VSYNC at or after the buffer timestamp). For this to work, the timestamp + * needs to be <i>reasonably close</i> to the current {@link System#nanoTime}. + * Currently, this is set as within one (1) second. A few notes: + * + * <ul> + * <li>the buffer will not be returned to the codec until the timestamp + * has passed and the buffer is no longer used by the {@link android.view.Surface}. + * <li>buffers are processed sequentially, so you may block subsequent buffers to + * be displayed on the {@link android.view.Surface}. This is important if you + * want to react to user action, e.g. stop the video or seek. + * <li>if multiple buffers are sent to the {@link android.view.Surface} to be + * rendered at the same VSYNC, the last one will be shown, and the other ones + * will be dropped. + * <li>if the timestamp is <em>not</em> "reasonably close" to the current system + * time, the {@link android.view.Surface} will ignore the timestamp, and + * display the buffer at the earliest feasible time. In this mode it will not + * drop frames. + * <li>for best performance and quality, call this method when you are about + * two VSYNCs' time before the desired render time. For 60Hz displays, this is + * about 33 msec. + * </ul> + * </td></tr> + * </table> + * + * @param index The index of a client-owned output buffer previously returned + * from a call to {@link #dequeueOutputBuffer}. + * @param renderTimestampNs The timestamp to associate with this buffer when + * it is sent to the Surface. + */ + public final void releaseOutputBuffer(int index, long renderTimestampNs) { + releaseOutputBuffer( + index, true /* render */, true /* updatePTS */, renderTimestampNs); + } + + private native final void releaseOutputBuffer( + int index, boolean render, boolean updatePTS, long timeNs); /** * Signals end-of-stream on input. Equivalent to submitting an empty buffer with diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index a710c03..4a7c096 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -260,7 +260,11 @@ status_t JMediaCodec::dequeueOutputBuffer( return OK; } -status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { +status_t JMediaCodec::releaseOutputBuffer( + size_t index, bool render, bool updatePTS, int64_t timestampNs) { + if (updatePTS) { + return mCodec->renderOutputBufferAndRelease(index, timestampNs); + } return render ? mCodec->renderOutputBufferAndRelease(index) : mCodec->releaseOutputBuffer(index); @@ -873,7 +877,8 @@ static jint android_media_MediaCodec_dequeueOutputBuffer( } static void android_media_MediaCodec_releaseOutputBuffer( - JNIEnv *env, jobject thiz, jint index, jboolean render) { + JNIEnv *env, jobject thiz, + jint index, jboolean render, jboolean updatePTS, jlong timestampNs) { ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); sp<JMediaCodec> codec = getMediaCodec(env, thiz); @@ -883,7 +888,7 @@ static void android_media_MediaCodec_releaseOutputBuffer( return; } - status_t err = codec->releaseOutputBuffer(index, render); + status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs); throwExceptionAsNecessary(env, err); } @@ -1138,7 +1143,7 @@ static JNINativeMethod gMethods[] = { { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", (void *)android_media_MediaCodec_dequeueOutputBuffer }, - { "releaseOutputBuffer", "(IZ)V", + { "releaseOutputBuffer", "(IZZJ)V", (void *)android_media_MediaCodec_releaseOutputBuffer }, { "signalEndOfInputStream", "()V", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 2f2ea96..bf9f4ea 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -79,7 +79,8 @@ struct JMediaCodec : public AHandler { status_t dequeueOutputBuffer( JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs); - status_t releaseOutputBuffer(size_t index, bool render); + status_t releaseOutputBuffer( + size_t index, bool render, bool updatePTS, int64_t timestampNs); status_t signalEndOfInputStream(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index b2b2bd8..9069a55 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -436,6 +436,8 @@ public class DirectoryFragment extends Fragment { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenuInflater().inflate(R.menu.mode_directory, menu); + mode.setTitle(getResources() + .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount())); return true; } diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml index 546ddd4..0d943ed 100644 --- a/packages/Keyguard/res/layout/keyguard_status_view.xml +++ b/packages/Keyguard/res/layout/keyguard_status_view.xml @@ -28,7 +28,7 @@ androidprv:layout_maxWidth="@dimen/keyguard_security_width" androidprv:layout_maxHeight="@dimen/keyguard_security_height" android:gravity="center_horizontal|top" - android:layout_marginTop="32dp" + android:layout_marginTop="48dp" android:layout_marginBottom="32dp" android:contentDescription="@string/keyguard_accessibility_status"> <LinearLayout diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index 54dde82..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png Binary files differdeleted file mode 100644 index 3c0dc4e..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png Binary files differdeleted file mode 100644 index 3b1944d..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png Binary files differdeleted file mode 100644 index 693abf5..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index c526433..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index d13bc69..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index a137a80..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index 8da7945..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index 7cb52e3..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png Binary files differdeleted file mode 100644 index 8010ce7..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png Binary files differdeleted file mode 100644 index 807f607..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png Binary files differdeleted file mode 100644 index 15340d3..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index b9afa44..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png Binary files differdeleted file mode 100644 index 6d46fdd..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png Binary files differdeleted file mode 100644 index e562bc2..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png Binary files differdeleted file mode 100644 index e3cc9b0..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png Binary files differdeleted file mode 100644 index afdee8f..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png Binary files differdeleted file mode 100644 index 7742207..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png Binary files differdeleted file mode 100644 index a2e8fe1..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png Binary files differdeleted file mode 100644 index e15981a..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml new file mode 100644 index 0000000..5c38a22 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml @@ -0,0 +1,29 @@ +<!-- 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > +<size +android:width="24dp" +android:height="24dp"/> + + <viewport android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + +<group> +<path + android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" + android:fill="#ffffffff" + /> +</group> +</vector> diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml index f4d7033..09d0d7d 100644 --- a/packages/SystemUI/res/layout/status_bar_flip_button.xml +++ b/packages/SystemUI/res/drawable/notification_header_bg.xml @@ -15,11 +15,17 @@ ~ limitations under the License --> -<ImageView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/settings_button" - android:layout_width="50dp" - android:layout_height="50dp" - android:scaleType="center" - android:src="@drawable/ic_notify_quicksettings" - android:background="@drawable/ic_notify_button_bg" - android:contentDescription="@string/accessibility_desc_quick_settings"/>
\ No newline at end of file +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <shape> + <solid android:color="@color/background_color_1_press" /> + <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> + </shape> + </item> + <item> + <shape> + <solid android:color="@color/background_color_1" /> + <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml new file mode 100644 index 0000000..c015cc8 --- /dev/null +++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml @@ -0,0 +1,35 @@ +<?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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:versionCode="1" > + + <size + android:height="16dp" + android:width="16dp" /> + + <viewport + android:viewportHeight="100" + android:viewportWidth="100" /> + + <group> + <path + android:name="x" + android:pathData="M0,0L100,100M0,100L100,0z" + android:stroke="@color/recents_task_bar_dark_dismiss_color" + android:strokeWidth="8.0" + android:strokeLineCap="square" /> + </group> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml new file mode 100644 index 0000000..9c93db9 --- /dev/null +++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml @@ -0,0 +1,35 @@ +<?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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:versionCode="1" > + + <size + android:height="16dp" + android:width="16dp" /> + + <viewport + android:viewportHeight="100" + android:viewportWidth="100" /> + + <group> + <path + android:name="x" + android:pathData="M0,0L100,100M0,100L100,0z" + android:stroke="@color/recents_task_bar_light_dismiss_color" + android:strokeWidth="8.0" + android:strokeLineCap="square" /> + </group> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml index e4954e7..7d9cfa1 100644 --- a/packages/SystemUI/res/layout/heads_up.xml +++ b/packages/SystemUI/res/layout/heads_up.xml @@ -20,7 +20,7 @@ <com.android.systemui.statusbar.policy.HeadsUpNotificationView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="@dimen/notification_panel_width" + android:layout_width="match_parent" android:id="@+id/content_holder" android:background="@drawable/notification_panel_bg" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index b7df51d..1efda8c 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -29,7 +29,7 @@ <com.android.systemui.settings.ToggleSlider android:id="@+id/brightness_slider" android:layout_width="0dp" - android:layout_height="40dp" + android:layout_height="44dp" android:layout_gravity="center_vertical" android:layout_weight="1" systemui:text="@string/status_bar_settings_auto_brightness_label" /> diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index f7df18eb..bda6431 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -63,6 +63,13 @@ android:maxLines="2" android:ellipsize="marquee" android:fadingEdge="horizontal" /> + <ImageView + android:id="@+id/dismiss_task" + android:layout_width="@dimen/recents_task_view_application_icon_size" + android:layout_height="@dimen/recents_task_view_application_icon_size" + android:layout_gravity="center_vertical|end" + android:padding="23dp" + android:src="@drawable/recents_dismiss_dark" /> </com.android.systemui.recents.views.TaskBarView> </com.android.systemui.recents.views.TaskView> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 1b35537..585658e 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -77,26 +77,24 @@ <LinearLayout android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" - android:orientation="horizontal"> - - <LinearLayout android:id="@+id/statusIcons" + android:orientation="horizontal" + > + <LinearLayout android:id="@+id/system_icons" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:orientation="horizontal"/> - - <LinearLayout - android:id="@+id/signal_battery_cluster" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingStart="2dp" - android:orientation="horizontal" - android:gravity="center" > - <include layout="@layout/signal_cluster_view" + <LinearLayout android:id="@+id/statusIcons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal"/> + + <include layout="@layout/signal_cluster_view" android:id="@+id/signal_cluster" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="2dp" /> <!-- battery must be padded below to match assets --> <com.android.systemui.BatteryMeterView @@ -107,7 +105,6 @@ android:layout_marginStart="4dip" /> </LinearLayout> - <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:textAppearance="@style/TextAppearance.StatusBar.Clock" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 3267c36..f045da4 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -22,7 +22,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/notification_panel" - android:layout_width="0dp" + android:layout_width="match_parent" android:layout_height="match_parent" > @@ -34,15 +34,6 @@ android:layout_gravity="bottom" /> - <com.android.keyguard.CarrierText - android:id="@+id/keyguard_carrier_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="2dp" - android:layout_marginLeft="8dp" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" /> - <include layout="@layout/keyguard_status_view" android:layout_height="wrap_content" @@ -59,9 +50,8 @@ /> <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer + style="@style/NotificationsQuickSettings" android:id="@+id/notification_container_parent" - android:layout_width="match_parent" - android:layout_height="wrap_content" android:clipToPadding="false" android:clipChildren="false"> @@ -98,11 +88,7 @@ </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> - - <include layout="@layout/status_bar_expanded_header" - android:layout_width="match_parent" - android:layout_height="@dimen/status_bar_header_height" - /> + <include layout="@layout/status_bar_expanded_header" /> <include layout="@layout/keyguard_bottom_area" diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 460dd4b..89fa988 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -20,11 +20,12 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/header" - android:layout_width="match_parent" + style="@style/StatusBarHeader" android:layout_height="@dimen/status_bar_header_height" - android:orientation="horizontal" - android:gravity="center_vertical" + android:paddingStart="@dimen/notification_side_padding" + android:paddingEnd="@dimen/notification_side_padding" android:baselineAligned="false" + android:elevation="10dp" > <View @@ -37,10 +38,12 @@ <RelativeLayout android:id="@+id/datetime" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_gravity="start" - android:paddingStart="8dp" - android:paddingEnd="8dp" + android:paddingTop="16dp" + android:paddingBottom="16dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" android:background="@drawable/ic_notify_button_bg" android:enabled="false" > @@ -48,10 +51,9 @@ android:id="@+id/clock" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="8dp" android:singleLine="true" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" - android:layout_centerVertical="true" + systemui:amPmStyle="normal" /> <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" @@ -59,11 +61,49 @@ android:layout_height="wrap_content" android:singleLine="true" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" - android:layout_toEndOf="@id/clock" - android:layout_alignBaseline="@id/clock" + android:layout_below="@id/clock" /> </RelativeLayout> + <com.android.keyguard.CarrierText + android:id="@+id/keyguard_carrier_text" + android:layout_width="wrap_content" + android:layout_height="@dimen/status_bar_header_height_keyguard" + android:layout_marginLeft="8dp" + android:gravity="center_vertical" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" + android:layout_width="40dp" + android:layout_height="@dimen/status_bar_header_height" + android:layout_alignParentEnd="true" + android:background="@null" + android:scaleType="centerInside" + android:padding="6dp" + /> + + <ImageButton android:id="@+id/settings_button" + style="@android:style/Widget.Quantum.Button.Borderless" + android:layout_toStartOf="@id/multi_user_switch" + android:layout_width="56dp" + android:layout_height="@dimen/status_bar_header_height" + android:src="@drawable/ic_settings_24dp" + android:contentDescription="@string/accessibility_desc_quick_settings"/> + + <FrameLayout android:id="@+id/system_icons_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/status_bar_header_height" + android:layout_toStartOf="@id/multi_user_switch" + android:layout_marginEnd="4dp" + /> + + <include + layout="@layout/quick_settings_brightness_dialog" + android:id="@+id/brightness_container" + android:layout_width="match_parent" + /> + <TextView android:id="@+id/header_debug_info" android:visibility="invisible" @@ -77,22 +117,4 @@ android:padding="2dp" /> - <include layout="@layout/status_bar_flip_button" - android:id="@+id/header_flipper" - android:layout_width="50dp" - android:layout_height="50dp" - android:layout_alignParentEnd="true"/> - - <ImageView android:id="@+id/clear_all_button" - android:layout_width="50dp" - android:layout_height="50dp" - android:layout_toStartOf="@id/header_flipper" - android:scaleType="center" - android:src="@drawable/ic_notify_clear" - android:background="@drawable/ic_notify_button_bg" - android:contentDescription="@string/accessibility_clear_all" - /> - - - </com.android.systemui.statusbar.phone.StatusBarHeaderView> diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml index e6d7c93..0e84762 100644 --- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml +++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml @@ -28,6 +28,7 @@ android:layout_alignParentTop="true" android:layout_alignParentBottom="true" android:button="@null" + android:background="@*android:drawable/switch_track_quantum" /> <com.android.systemui.settings.ToggleSeekBar android:id="@+id/slider" diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index f9b022c..26616cd 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -35,9 +35,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > <include layout="@layout/status_bar_expanded" - android:layout_width="@dimen/notification_panel_width" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_gravity="start|top" /> + android:visibility="gone" /> </com.android.systemui.statusbar.phone.PanelHolder> </com.android.systemui.statusbar.phone.StatusBarWindowView> diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml index bc56cf6..70c5042 100644 --- a/packages/SystemUI/res/layout/user_switcher_host.xml +++ b/packages/SystemUI/res/layout/user_switcher_host.xml @@ -22,7 +22,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#dd000000"> + android:background="#dd000000" + android:elevation="12dp"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml index b7becac..d4a9986 100644 --- a/packages/SystemUI/res/values-sw600dp/styles.xml +++ b/packages/SystemUI/res/values-sw600dp/styles.xml @@ -18,4 +18,15 @@ <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer"> <item name="android:layout_width">480dp</item> </style> + + <style name="NotificationsQuickSettings"> + <item name="android:layout_width">@dimen/notification_panel_width</item> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_gravity">top|center_horizontal</item> + </style> + + <style name="StatusBarHeader"> + <item name="android:layout_width">@dimen/notification_panel_width</item> + <item name="android:layout_gravity">center_horizontal</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index f5674d2..8fd1206 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -51,6 +51,13 @@ <enum name="end" value="1" /> </attr> </declare-styleable> + <declare-styleable name="Clock"> + <attr name="amPmStyle" format="enum"> + <enum name="normal" value="0" /> + <enum name="small" value="1" /> + <enum name="gone" value="2" /> + </attr> + </declare-styleable> <attr name="orientation"> <enum name="horizontal" value="0" /> <enum name="vertical" value="1" /> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 9281265..c1a4e26 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -30,7 +30,6 @@ <drawable name="recents_callout_line">#99ffffff</drawable> <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable> <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable> - <drawable name="notification_header_bg">#FF000000</drawable> <color name="notification_panel_scrim_color">#A0000000</color> <color name="notification_panel_scrim_color_keyguard">#80000000</color> <color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white --> @@ -53,10 +52,18 @@ <!-- The default recents task bar background color. --> <color name="recents_task_bar_default_background_color">#e6444444</color> <!-- The default recents task bar text color. --> - <color name="recents_task_bar_default_text_color">#ffffffff</color> + <color name="recents_task_bar_default_text_color">#ffeeeeee</color> <!-- The recents task bar light text color to be drawn on top of dark backgrounds. --> - <color name="recents_task_bar_light_text_color">#ffffffff</color> + <color name="recents_task_bar_light_text_color">#ffeeeeee</color> <!-- The recents task bar dark text color to be drawn on top of light backgrounds. --> <color name="recents_task_bar_dark_text_color">#ff222222</color> + <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. --> + <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color> + <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. --> + <color name="recents_task_bar_dark_dismiss_color">#ff333333</color> + <!-- Our quantum color palette (deep teal) --> + <color name="primary_color">#ff7fcac3</color> + <color name="background_color_1">#ff384248</color> + <color name="background_color_1_press">#ff54656e</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c0376f0..21eb41c 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -119,7 +119,7 @@ <!-- The animation duration for animating in the info pane. --> <integer name="recents_animate_task_view_info_pane_duration">150</integer> <!-- The animation duration for animating the removal of a task view. --> - <integer name="recents_animate_task_view_remove_duration">150</integer> + <integer name="recents_animate_task_view_remove_duration">250</integer> <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. --> <integer name="recents_max_task_stack_view_dim">96</integer> <!-- Transposes the search bar layout in landscape --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c6fdc16..ab34030 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -153,11 +153,14 @@ <dimen name="close_handle_underlap">32dp</dimen> <!-- Height of the status bar header bar --> - <dimen name="status_bar_header_height">48dp</dimen> + <dimen name="status_bar_header_height">56dp</dimen> <!-- Height of the status bar header bar when expanded --> <dimen name="status_bar_header_height_expanded">144dp</dimen> + <!-- Height of the status bar header bar when on Keyguard --> + <dimen name="status_bar_header_height_keyguard">40dp</dimen> + <!-- Gravity for the notification panel --> <!-- 0x37 = fill_horizontal|top --> <integer name="notification_panel_layout_gravity">0x37</integer> @@ -196,9 +199,6 @@ <!-- Quick Settings CA Cert Warning tile geometry: gap between icon and text --> <dimen name="qs_cawarn_tile_margin_below_icon">3dp</dimen> - <!-- The width of the notification panel window: match_parent below sw600dp --> - <dimen name="notification_panel_width">-1dp</dimen> - <!-- used by DessertCase --> <dimen name="dessert_case_cell_size">192dp</dimen> @@ -224,7 +224,7 @@ <dimen name="recents_task_view_z_increment">5dp</dimen> <!-- The amount to translate when animating the removal of a task. --> - <dimen name="recents_task_view_remove_anim_translation_x">75dp</dimen> + <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen> <!-- The amount of space a user has to scroll to dismiss any info panes. --> <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen> @@ -243,7 +243,10 @@ <dimen name="top_stack_peek_amount">12dp</dimen> <!-- Space reserved for the cards behind the top card in the bottom stack --> - <dimen name="bottom_stack_peek_amount">18dp</dimen> + <dimen name="bottom_stack_peek_amount">12dp</dimen> + + <!-- The height of the area before the bottom stack in which the notifications slow down --> + <dimen name="bottom_stack_slow_down_length">12dp</dimen> <!-- The side padding of the notifications--> <dimen name="notification_side_padding">8dp</dimen> @@ -251,8 +254,11 @@ <!-- Z distance between notifications if they are in the stack --> <dimen name="z_distance_between_notifications">2dp</dimen> + <!-- The padding between the individual notification cards when dimmed. --> + <dimen name="notification_padding_dimmed">0dp</dimen> + <!-- The padding between the individual notification cards. --> - <dimen name="notification_padding">3dp</dimen> + <dimen name="notification_padding">4dp</dimen> <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) --> <dimen name="collapsed_stack_height">94dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 8ab646d..4f52870 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -69,8 +69,7 @@ <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" /> <style name="TextAppearance.StatusBar.Expanded.Clock"> - <item name="android:textSize">32dp</item> - <item name="android:fontFamily">sans-serif-light</item> + <item name="android:textSize">18dp</item> <item name="android:textStyle">normal</item> <item name="android:textColor">#ffffff</item> </style> @@ -78,8 +77,7 @@ <style name="TextAppearance.StatusBar.Expanded.Date"> <item name="android:textSize">12dp</item> <item name="android:textStyle">normal</item> - <item name="android:textColor">#cccccc</item> - <item name="android:textAllCaps">true</item> + <item name="android:textColor">#afb3b6</item> </style> <style name="TextAppearance.StatusBar.Expanded.Network" parent="@style/TextAppearance.StatusBar.Expanded.Date"> @@ -138,6 +136,7 @@ <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:padding">16dp</item> + <item name="android:layout_alignParentBottom">true</item> </style> <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer" /> @@ -169,6 +168,16 @@ <item name="android:textSize">14dp</item> </style> - <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" /> + <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault"> + <item name="android:colorPrimary">@color/primary_color</item> + </style> + <style name="NotificationsQuickSettings"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">match_parent</item> + </style> + + <style name="StatusBarHeader"> + <item name="android:layout_width">match_parent</item> + </style> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 3ef8316..19a1b11 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -255,15 +255,10 @@ public class AlternateRecentsComponent { /** Loads the first task thumbnail */ Bitmap loadFirstTaskThumbnail() { SystemServicesProxy ssp = mSystemServicesProxy; - List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1, - UserHandle.CURRENT.getIdentifier()); - for (ActivityManager.RecentTaskInfo t : tasks) { - // Skip tasks in the home stack - if (ssp.isInHomeStack(t.persistentId)) { - return null; - } + List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1); - return ssp.getTaskThumbnail(t.persistentId); + for (ActivityManager.RunningTaskInfo t : tasks) { + return ssp.getTaskThumbnail(t.id); } return null; } @@ -286,17 +281,6 @@ public class AlternateRecentsComponent { return (tasks.size() > 1); } - /** Returns whether the base intent of the top task stack was launched with the flag - * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */ - boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) { - if (tasks.size() > 0) { - ActivityManager.RecentTaskInfo t = tasks.get(0); - Console.log(t.baseIntent.toString()); - return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; - } - return false; - } - /** Converts from the device rotation to the degree */ float getDegreesForRotation(int value) { switch (value) { @@ -416,16 +400,14 @@ public class AlternateRecentsComponent { } // Otherwise, Recents is not the front-most activity and we should animate into it. If - // the activity at the root of the top task stack is excluded from recents, or if that - // task stack is in the home stack, then we just do a simple transition. Otherwise, we - // animate to the rects defined by the Recents service, which can differ depending on the - // number of items in the list. + // the activity at the root of the top task stack in the home stack, then we just do a + // simple transition. Otherwise, we animate to the rects defined by the Recents service, + // which can differ depending on the number of items in the list. List<ActivityManager.RecentTaskInfo> recentTasks = - ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier()); + ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier()); Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect; - boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks); - boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents && + boolean useThumbnailTransition = !isTopTaskHome && hasValidTaskRects(); if (useThumbnailTransition) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java deleted file mode 100644 index 95ab8e8..0000000 --- a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.android.systemui.recents; - -import android.animation.TimeInterpolator; - -/** - * A pre-baked bezier-curved interpolator for quantum-paper transitions. - */ -public class BakedBezierInterpolator implements TimeInterpolator { - public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator(); - - /** - * Use the INSTANCE variable instead of instantiating. - */ - private BakedBezierInterpolator() { - super(); - } - - /** - * Lookup table values. - * Generated using a Bezier curve from (0,0) to (1,1) with control points: - * P0 (0,0) - * P1 (0.4, 0) - * P2 (0.2, 1.0) - * P3 (1.0, 1.0) - * - * Values sampled with x at regular intervals between 0 and 1. - */ - private static final float[] VALUES = new float[] { - 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f, - 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f, - 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f, - 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f, - 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f, - 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f, - 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f, - 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f, - 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f, - 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f - }; - - private static final float STEP_SIZE = 1.0f / (VALUES.length - 1); - - @Override - public float getInterpolation(float input) { - if (input >= 1.0f) { - return 1.0f; - } - - if (input <= 0f) { - return 0f; - } - - int position = Math.min( - (int)(input * (VALUES.length - 1)), - VALUES.length - 2); - - float quantized = position * STEP_SIZE; - float difference = input - quantized; - float weight = difference / STEP_SIZE; - - return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]); - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 1d6a76c..90998da 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -32,7 +32,7 @@ public class Constants { // Enables the use of theme colors as the task bar background public static final boolean EnableTaskBarThemeColors = true; // Enables the info pane on long-press - public static final boolean EnableInfoPane = true; + public static final boolean EnableInfoPane = false; // Enables the search bar layout public static final boolean EnableSearchLayout = true; // Enables the dynamic shadows behind each task diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 463cf74..9afc1cb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -23,6 +23,8 @@ import android.content.res.Resources; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import com.android.systemui.R; @@ -42,6 +44,8 @@ public class RecentsConfiguration { public float animationPxMovementPerSecond; + public Interpolator defaultBezierInterpolator; + public int filteringCurrentViewsMinAnimDuration; public int filteringNewViewsMinAnimDuration; public int taskBarEnterAnimDuration; @@ -121,7 +125,6 @@ public class RecentsConfiguration { res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment); searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); - taskBarViewDefaultBackgroundColor = res.getColor(R.color.recents_task_bar_default_background_color); taskBarViewDefaultTextColor = @@ -131,6 +134,9 @@ public class RecentsConfiguration { taskBarViewDarkTextColor = res.getColor(R.color.recents_task_bar_dark_text_color); + defaultBezierInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + // Update the search widget id SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0); searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index f3e411f..1ca0476 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -35,7 +35,6 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -362,7 +361,7 @@ public class RecentsTaskLoader { return mSystemServicesProxy; } - private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) { + private List<ActivityManager.RecentTaskInfo> getRecentTasks() { long t1 = System.currentTimeMillis(); SystemServicesProxy ssp = mSystemServicesProxy; @@ -375,23 +374,6 @@ public class RecentsTaskLoader { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|tasks]", "" + tasks.size()); - // Remove home/recents tasks - Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); - while (iter.hasNext()) { - ActivityManager.RecentTaskInfo t = iter.next(); - - // Skip tasks in the home stack - if (ssp.isInHomeStack(t.persistentId)) { - iter.remove(); - continue; - } - // Skip tasks from this Recents package - if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) { - iter.remove(); - continue; - } - } - return tasks; } @@ -408,7 +390,7 @@ public class RecentsTaskLoader { // Get the recent tasks SystemServicesProxy ssp = mSystemServicesProxy; - List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context); + List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(); // Add each task to the task stack t1 = System.currentTimeMillis(); @@ -554,14 +536,16 @@ public class RecentsTaskLoader { } /** Completely removes the resource data from the pool. */ - public void deleteTaskData(Task t) { + public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|deleteTask]", t); mLoadQueue.removeTask(t); mThumbnailCache.remove(t.key); mApplicationIconCache.remove(t.key); - t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); + if (notifyTaskDataUnloaded) { + t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); + } } /** Stops the task loader and clears all pending tasks */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index b41555f..8d82883 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -30,7 +30,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -40,6 +39,7 @@ import android.os.UserManager; import android.util.Pair; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Random; @@ -54,7 +54,7 @@ public class SystemServicesProxy { IPackageManager mIpm; UserManager mUm; SearchManager mSm; - String mPackage; + String mRecentsPackage; ComponentName mAssistComponent; Bitmap mDummyIcon; @@ -67,7 +67,7 @@ public class SystemServicesProxy { mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); mIpm = AppGlobals.getPackageManager(); mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); - mPackage = context.getPackageName(); + mRecentsPackage = context.getPackageName(); // Resolve the assist intent Intent assist = mSm.getAssistIntent(context, false); @@ -83,14 +83,14 @@ public class SystemServicesProxy { } /** Returns a list of the recents tasks */ - public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) { + public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId) { if (mAm == null) return null; // If we are mocking, then create some recent tasks if (Constants.DebugFlags.App.EnableSystemServicesProxy) { ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<ActivityManager.RecentTaskInfo>(); - int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount); + int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount); for (int i = 0; i < count; i++) { // Create a dummy component name int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount; @@ -114,9 +114,43 @@ public class SystemServicesProxy { return tasks; } - return mAm.getRecentTasksForUser(numTasks, + // Remove home/recents/excluded tasks + int minNumTasksToQuery = 10; + int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); + List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, ActivityManager.RECENT_IGNORE_UNAVAILABLE | - ActivityManager.RECENT_INCLUDE_PROFILES, userId); + ActivityManager.RECENT_INCLUDE_PROFILES | + ActivityManager.RECENT_WITH_EXCLUDED, userId); + boolean isFirstValidTask = true; + Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); + while (iter.hasNext()) { + ActivityManager.RecentTaskInfo t = iter.next(); + + // NOTE: The order of these checks happens in the expected order of the traversal of the + // tasks + + // Skip tasks from this Recents package + if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) { + iter.remove(); + continue; + } + // Check the first non-recents task, include this task even if it is marked as excluded + // from recents. In other words, only remove excluded tasks if it is not the first task + boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + if (isExcluded && !isFirstValidTask) { + iter.remove(); + continue; + } + isFirstValidTask = false; + // Skip tasks in the home stack + if (isInHomeStack(t.persistentId)) { + iter.remove(); + continue; + } + } + + return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); } /** Returns a list of the running tasks */ @@ -165,11 +199,12 @@ public class SystemServicesProxy { } /** Removes the task and kills the process */ - public void removeTask(int taskId) { + public void removeTask(int taskId, boolean isDocument) { if (mAm == null) return; if (Constants.DebugFlags.App.EnableSystemServicesProxy) return; - mAm.removeTask(taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS); + // Remove the task, and only kill the process if it is not a document + mAm.removeTask(taskId, isDocument ? 0 : ActivityManager.REMOVE_TASK_KILL_PROCESS); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java index b602f84..46e6ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java @@ -18,6 +18,7 @@ package com.android.systemui.recents; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.Drawable; /* Common code */ public class Utilities { @@ -54,12 +55,15 @@ public class Utilities { 0.0722f * Color.blue(color)); } - /** Returns the ideal text color to draw on top of a specified background color. */ - public static int getIdealTextColorForBackgroundColor(int color) { - RecentsConfiguration configuration = RecentsConfiguration.getInstance(); + /** Returns the ideal color to draw on top of a specified background color. */ + public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) { int greyscale = colorToGreyscale(color); - return (greyscale < 128) ? configuration.taskBarViewLightTextColor : - configuration.taskBarViewDarkTextColor; - + return (greyscale < 128) ? lightRes : darkRes; + } + /** Returns the ideal drawable to draw on top of a specified background color. */ + public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes, + Drawable darkRes) { + int greyscale = colorToGreyscale(color); + return (greyscale < 128) ? lightRes : darkRes; } } 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 8168619..a6d7e67 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -346,7 +346,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV RecentsTaskLoader.getInstance().getSystemServicesProxy() .moveTaskToFront(task.key.id, opts); } else { - // Launch the activity with the desired animation + // Launch the activity anew with the desired animation Intent i = new Intent(task.key.baseIntent); i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_TASK_ON_HOME @@ -361,6 +361,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } catch (ActivityNotFoundException anfe) { Console.logError(getContext(), "Could not start Activity"); } + + // And clean up the old task + onTaskRemoved(task); } Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, @@ -390,6 +393,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV .addNextIntentWithParentStack(intent).startActivities(); } + @Override + public void onTaskRemoved(Task t) { + // Remove any stored data from the loader. We currently don't bother notifying the views + // that the data has been unloaded because at the point we call onTaskRemoved(), the views + // either don't need to be updated, or have already been removed. + RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); + loader.deleteTaskData(t, false); + + // Remove the old task from activity manager + int flags = t.key.baseIntent.getFlags(); + boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == + Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id, + isDocument); + } + /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ @Override 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 c6cb812..07caa1b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -16,7 +16,10 @@ package com.android.systemui.recents.views; +import android.animation.ValueAnimator; import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.ImageView; @@ -32,9 +35,13 @@ import com.android.systemui.recents.model.Task; class TaskBarView extends FrameLayout { Task mTask; + ImageView mDismissButton; ImageView mApplicationIcon; TextView mActivityDescription; + Drawable mLightDismissDrawable; + Drawable mDarkDismissDrawable; + public TaskBarView(Context context) { this(context, null); } @@ -49,6 +56,9 @@ class TaskBarView extends FrameLayout { public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + Resources res = context.getResources(); + mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light); + mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark); } @Override @@ -56,6 +66,28 @@ class TaskBarView extends FrameLayout { // Initialize the icon and description views mApplicationIcon = (ImageView) findViewById(R.id.application_icon); mActivityDescription = (TextView) findViewById(R.id.activity_description); + mDismissButton = (ImageView) findViewById(R.id.dismiss_task); + } + + /** Synchronizes this bar view's properties with the task's transform */ + void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, + TaskViewTransform toTransform, int duration) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); + if (duration > 0) { + if (animateFromTransform != null) { + mDismissButton.setAlpha(animateFromTransform.dismissAlpha); + } + mDismissButton.animate() + .alpha(toTransform.dismissAlpha) + .setStartDelay(0) + .setDuration(duration) + .setInterpolator(config.defaultBezierInterpolator) + .withLayer() + .start(); + } else { + mDismissButton.setAlpha(toTransform.dismissAlpha); + } + mDismissButton.invalidate(); } /** Binds the bar view to the task */ @@ -74,7 +106,10 @@ class TaskBarView extends FrameLayout { int tint = t.colorPrimary; if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) { setBackgroundColor(tint); - mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint)); + mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, + configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor)); + mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, + mLightDismissDrawable, mDarkDismissDrawable)); } else { setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor); mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java index c6c29a6..f1c362a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java @@ -30,7 +30,6 @@ import android.util.AttributeSet; import android.widget.Button; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.Utilities; @@ -111,7 +110,8 @@ class TaskInfoView extends FrameLayout { int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius); mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius); mCircularClipAnimator.setDuration(duration); - mCircularClipAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mCircularClipAnimator.setInterpolator( + RecentsConfiguration.getInstance().defaultBezierInterpolator); mCircularClipAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -143,7 +143,7 @@ class TaskInfoView extends FrameLayout { .scaleX(1f) .scaleY(1f) .setDuration(duration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator) .withLayer() .start(); } 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 ad0f2f82..b64225e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -23,6 +23,7 @@ import android.animation.ValueAnimator; import android.app.Activity; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.Region; @@ -36,7 +37,6 @@ import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.OverScroller; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; @@ -60,6 +60,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal interface TaskStackViewCallbacks { public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t); public void onTaskAppInfoLaunched(Task t); + public void onTaskRemoved(Task t); } TaskStack mStack; @@ -168,6 +169,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset); } + // Set the alphas + transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f; + // Update the rect and visibility transform.rect.set(mTaskRect); if (t < -(numPeekCards + 1)) { @@ -336,7 +340,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll - curScroll, 250)); - mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -1034,6 +1038,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + @Override + public void onTaskDismissed(TaskView tv) { + Task task = tv.getTask(); + // Remove the task from the view + mStack.removeTask(task); + // Notify the callback that we've removed the task and it can clean up after it + mCb.onTaskRemoved(task); + } + /**** View.OnClickListener Implementation ****/ @Override @@ -1093,6 +1106,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void onComponentRemoved(Set<ComponentName> cns) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); // For other tasks, just remove them directly if they no longer exist ArrayList<Task> tasks = mStack.getTasks(); for (int i = tasks.size() - 1; i >= 0; i--) { @@ -1475,17 +1489,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { @Override public void onChildDismissed(View v) { TaskView tv = (TaskView) v; - Task task = tv.getTask(); - - // Remove the task from the view - mSv.mStack.removeTask(task); - - // Remove any stored data from the loader - RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); - loader.deleteTaskData(task); - - // Remove the task from activity manager - RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(tv.getTask().key.id); + mSv.onTaskDismissed(tv); // Disable HW layers mSv.decHwLayersRefCount("swipeComplete"); 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 b03f389..5fad629 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -31,7 +31,6 @@ import android.view.View; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.model.Task; @@ -46,6 +45,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, public void onTaskInfoPanelShown(TaskView tv); public void onTaskInfoPanelHidden(TaskView tv); public void onTaskAppInfoClicked(TaskView tv); + public void onTaskDismissed(TaskView tv); // public void onTaskViewReboundToTask(TaskView tv, Task t); } @@ -143,6 +143,10 @@ public class TaskView extends FrameLayout implements View.OnClickListener, int minZ = config.taskViewTranslationZMinPx; int incZ = config.taskViewTranslationZIncrementPx; + // Update the bar view + mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration); + + // Update this task view if (duration > 0) { if (animateFromTransform != null) { setTranslationY(animateFromTransform.translationY); @@ -161,7 +165,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, .scaleY(toTransform.scale) .alpha(toTransform.alpha) .setDuration(duration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .withLayer() .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override @@ -221,8 +225,8 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mBarView.setAlpha(0f); mBarView.animate() .alpha(1f) - .setStartDelay(235) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setStartDelay(250) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskBarEnterAnimDuration) .withLayer() .start(); @@ -234,7 +238,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mBarView.animate() .alpha(0f) .setStartDelay(0) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskBarExitAnimDuration) .withLayer() .withEndAction(new Runnable() { @@ -252,7 +256,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, animate().translationX(config.taskViewRemoveAnimTranslationXPx) .alpha(0f) .setStartDelay(0) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskViewRemoveAnimDuration) .withLayer() .withEndAction(new Runnable() { @@ -310,7 +314,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mInfoView.animate() .alpha(0f) .setDuration(config.taskViewInfoPaneAnimDuration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .withLayer() .withEndAction(new Runnable() { @Override @@ -380,6 +384,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mInfoView.rebindToTask(mTask, reloadingTaskData); // Rebind any listeners mBarView.mApplicationIcon.setOnClickListener(this); + mBarView.mDismissButton.setOnClickListener(this); mInfoView.mAppInfoButton.setOnClickListener(this); } mTaskDataLoaded = true; @@ -405,6 +410,15 @@ public class TaskView extends FrameLayout implements View.OnClickListener, hideInfoPane(); } else if (v == mBarView.mApplicationIcon) { mCb.onTaskIconClicked(this); + } else if (v == mBarView.mDismissButton) { + // Animate out the view and call the callback + final TaskView tv = this; + animateRemoval(new Runnable() { + @Override + public void run() { + mCb.onTaskDismissed(tv); + } + }); } else if (v == mInfoView.mAppInfoButton) { mCb.onTaskAppInfoClicked(this); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index 0748bbb..e6391a8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -24,6 +24,7 @@ public class TaskViewTransform { public int translationY = 0; public float scale = 1f; public float alpha = 1f; + public float dismissAlpha = 1f; public boolean visible = false; public Rect rect = new Rect(); float t; @@ -36,6 +37,7 @@ public class TaskViewTransform { translationY = o.translationY; scale = o.scale; alpha = o.alpha; + dismissAlpha = o.dismissAlpha; visible = o.visible; rect.set(o.rect); t = o.t; @@ -44,6 +46,6 @@ public class TaskViewTransform { @Override public String toString() { return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha + - " visible: " + visible + " rect: " + rect; + " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha; } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 327e715..1747e6e 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -119,7 +119,6 @@ public class BrightnessController implements ToggleSlider.Listener { } }; mBrightnessObserver = new BrightnessObserver(mHandler); - mBrightnessObserver.startObserving(); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); @@ -128,13 +127,6 @@ public class BrightnessController implements ToggleSlider.Listener { mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power")); - - // Update the slider and mode before attaching the listener so we don't receive the - // onChanged notifications for the initial values. - updateMode(); - updateSlider(); - - control.setOnChangedListener(this); } public void addStateChangedCallback(BrightnessStateChangeCallback cb) { @@ -150,11 +142,24 @@ public class BrightnessController implements ToggleSlider.Listener { // Do nothing } + public void registerCallbacks() { + mBrightnessObserver.startObserving(); + mUserTracker.startTracking(); + + // Update the slider and mode before attaching the listener so we don't receive the + // onChanged notifications for the initial values. + updateMode(); + updateSlider(); + + mControl.setOnChangedListener(this); + } + /** Unregister all call backs, both to and from the controller */ public void unregisterCallbacks() { mBrightnessObserver.stopObserving(); mChangeCallbacks.clear(); mUserTracker.stopTracking(); + mControl.setOnChangedListener(null); } public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java index bd5e5e8..27881c4 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java @@ -92,6 +92,7 @@ public class BrightnessDialog extends Dialog implements mBrightnessController = new BrightnessController(getContext(), (ImageView) findViewById(R.id.brightness_icon), (ToggleSlider) findViewById(R.id.brightness_slider)); + mBrightnessController.registerCallbacks(); dismissBrightnessDialog(mBrightnessDialogLongTimeout); mBrightnessController.addStateChangedCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java index 036bd4f..f8ff616 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java @@ -29,9 +29,6 @@ public abstract class CurrentUserTracker extends BroadcastReceiver { private int mCurrentUserId; public CurrentUserTracker(Context context) { - IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); - context.registerReceiver(this, filter); - mCurrentUserId = ActivityManager.getCurrentUser(); mContext = context; } @@ -50,6 +47,12 @@ public abstract class CurrentUserTracker extends BroadcastReceiver { } } + public void startTracking() { + mCurrentUserId = ActivityManager.getCurrentUser(); + IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); + mContext.registerReceiver(this, filter); + } + public void stopTracking() { mContext.unregisterReceiver(this); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java index 7d38058..4b78072 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java @@ -62,7 +62,6 @@ public class ToggleSlider extends RelativeLayout { mToggle = (CompoundButton) findViewById(R.id.toggle); mToggle.setOnCheckedChangeListener(mCheckListener); - mToggle.setBackground(res.getDrawable(R.drawable.status_bar_toggle_button)); mSlider = (SeekBar) findViewById(R.id.slider); mSlider.setOnSeekBarChangeListener(mSeekListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index ecefc39..898f06e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1029,6 +1029,9 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void addNotificationViews(NotificationData.Entry entry) { + if (entry == null) { + return; + } // Add the expanded view and icon. int pos = mNotificationData.add(entry); if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 281bd2d..4bd0e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -112,7 +112,7 @@ public abstract class ExpandableView extends FrameLayout { * @return The desired notification height. */ public int getIntrinsicHeight() { - return mActualHeight; + return getHeight(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java index 864c597..451c5c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java @@ -34,21 +34,6 @@ public class NotificationOverflowContainer extends ActivatableNotificationView { } @Override - public void setActualHeight(int currentHeight, boolean notifyListeners) { - // noop - } - - @Override - public int getActualHeight() { - return getHeight(); - } - - @Override - public void setClipTopAmount(int clipTopAmount) { - // noop - } - - @Override protected void onFinishInflate() { super.onFinishInflate(); mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java new file mode 100644 index 0000000..c26f15e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -0,0 +1,90 @@ +/* + * 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 com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.ContactsContract; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; + +import com.android.systemui.R; +import com.android.systemui.settings.UserSwitcherHostView; +import com.android.systemui.statusbar.policy.UserInfoController; + +/** + * Image button for the multi user switcher. + */ +public class MultiUserSwitch extends ImageButton implements View.OnClickListener, + UserInfoController.OnUserInfoChangedListener { + + private ViewGroup mOverlayParent; + + public MultiUserSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setOnClickListener(this); + } + + public void setOverlayParent(ViewGroup parent) { + mOverlayParent = parent; + } + + @Override + public void onClick(View v) { + final UserManager um = UserManager.get(getContext()); + if (um.isUserSwitcherEnabled()) { + final UserSwitcherHostView switcher = + (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate( + R.layout.user_switcher_host, mOverlayParent, false); + switcher.setFinishRunnable(new Runnable() { + @Override + public void run() { + mOverlayParent.removeView(switcher); + } + }); + switcher.refreshUsers(); + mOverlayParent.addView(switcher); + } else { + Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent( + getContext(), v, ContactsContract.Profile.CONTENT_URI, + ContactsContract.QuickContact.MODE_LARGE, null); + getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } + } + + public void setUserInfoController(UserInfoController userInfoController) { + userInfoController.addListener(this); + } + + @Override + public void onUserInfoChanged(String name, Drawable picture) { + setImageDrawable(picture); + } +} 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 f63ba9c..6132ed2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -24,9 +24,11 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.widget.LinearLayout; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; @@ -56,10 +58,18 @@ public class NotificationPanelView extends PanelView implements private int mTrackingPointer; private VelocityTracker mVelocityTracker; private boolean mTracking; + + /** + * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't + * intercepted yet. + */ + private boolean mIntercepting; private boolean mQsExpanded; private float mInitialHeightOnTouch; private float mInitialTouchX; private float mInitialTouchY; + private float mLastTouchX; + private float mLastTouchY; private float mQsExpansionHeight; private int mQsMinExpansionHeight; private int mQsMaxExpansionHeight; @@ -93,6 +103,7 @@ public class NotificationPanelView extends PanelView implements super.onFinishInflate(); mHeader = (StatusBarHeaderView) findViewById(R.id.header); mHeader.getBackgroundView().setOnClickListener(this); + mHeader.setOverlayParent(this); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); mStackScrollerContainer = findViewById(R.id.notification_container_parent); mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container); @@ -144,7 +155,6 @@ public class NotificationPanelView extends PanelView implements public void setQsExpansionEnabled(boolean qsExpansionEnabled) { mQsExpansionEnabled = qsExpansionEnabled; - mHeader.setExpansionEnabled(qsExpansionEnabled); } public void closeQs() { @@ -193,6 +203,7 @@ public class NotificationPanelView extends PanelView implements switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + mIntercepting = true; mInitialTouchY = y; mInitialTouchX = x; initVelocityTracker(); @@ -215,6 +226,16 @@ public class NotificationPanelView extends PanelView implements case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); + if (mTracking) { + + // Already tracking because onOverscrolled was called. We need to update here + // so we don't stop for a frame until the next touch event gets handled in + // onTouchEvent. + setQsExpansion(h + mInitialHeightOnTouch); + trackMovement(event); + mIntercepting = false; + return true; + } if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) { onQsExpansionStarted(); @@ -222,14 +243,29 @@ public class NotificationPanelView extends PanelView implements mInitialTouchY = y; mInitialTouchX = x; mTracking = true; + mIntercepting = false; return true; } break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mIntercepting = false; + break; } return !mQsExpanded && super.onInterceptTouchEvent(event); } @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + + // Block request so we can still intercept the scrolling when QS is expanded. + if (!mQsExpanded) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + } + + @Override public boolean onTouchEvent(MotionEvent event) { // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference // implementation. @@ -297,11 +333,26 @@ public class NotificationPanelView extends PanelView implements return mQsExpanded || super.onTouchEvent(event); } + @Override + public void onOverscrolled(int amount) { + if (mIntercepting) { + onQsExpansionStarted(amount); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = mLastTouchY; + mInitialTouchX = mLastTouchX; + mTracking = true; + } + } + private void onQsExpansionStarted() { + onQsExpansionStarted(0); + } + + private void onQsExpansionStarted(int overscrollAmount) { cancelAnimation(); // Reset scroll position and apply that position to the expanded height. - float height = mQsExpansionHeight - mScrollView.getScrollY(); + float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount; mScrollView.scrollTo(0, 0); setQsExpansion(height); } @@ -359,6 +410,8 @@ public class NotificationPanelView extends PanelView implements private void trackMovement(MotionEvent event) { if (mVelocityTracker != null) mVelocityTracker.addMovement(event); + mLastTouchX = event.getX(); + mLastTouchY = event.getY(); } private void initVelocityTracker() { @@ -412,11 +465,8 @@ public class NotificationPanelView extends PanelView implements if (!mQsExpansionEnabled) { return false; } - View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded - ? mKeyguardStatusView - : mHeader; - boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight() - && y >= headerView.getTop() && y <= headerView.getBottom(); + boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight() + && y >= mHeader.getTop() && y <= mHeader.getBottom(); if (mQsExpanded) { return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0); } else { @@ -425,6 +475,35 @@ public class NotificationPanelView extends PanelView implements } @Override + public void setVisibility(int visibility) { + int oldVisibility = getVisibility(); + super.setVisibility(visibility); + if (visibility != oldVisibility) { + reparentStatusIcons(visibility == VISIBLE); + } + } + + /** + * When the notification panel gets expanded, we need to move the status icons in the header + * card. + */ + private void reparentStatusIcons(boolean toHeader) { + if (mStatusBar == null) { + return; + } + LinearLayout systemIcons = mStatusBar.getSystemIcons(); + if (systemIcons.getParent() != null) { + ((ViewGroup) systemIcons.getParent()).removeView(systemIcons); + } + if (toHeader) { + mHeader.attachSystemIcons(systemIcons); + } else { + mHeader.onSystemIconsDetached(); + mStatusBar.reattachSystemIcons(); + } + } + + @Override protected boolean isScrolledToBottom() { if (!isInSettings()) { return mNotificationStackScroller.isScrolledToBottom(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java index 46484f3..ba0b66e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -27,6 +27,7 @@ import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private Listener mListener; + private int mLastOverscrollAmount; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -58,7 +59,25 @@ public class ObservableScrollView extends ScrollView { } } + @Override + protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, + int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, + boolean isTouchEvent) { + mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY()); + return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, + maxOverScrollX, maxOverScrollY, isTouchEvent); + } + + @Override + protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { + super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); + if (mListener != null && mLastOverscrollAmount > 0) { + mListener.onOverscrolled(mLastOverscrollAmount); + } + } + public interface Listener { void onScrollChanged(); + void onOverscrolled(int amount); } } 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 92eee4e..4d09d6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -29,7 +29,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCE import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -112,6 +111,7 @@ import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; @@ -185,6 +185,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, LocationController mLocationController; NetworkController mNetworkController; RotationLockController mRotationLockController; + UserInfoController mUserInfoController; int mNaturalBarHeight = -1; int mIconSize = -1; @@ -205,6 +206,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // right-hand icons LinearLayout mSystemIconArea; + LinearLayout mSystemIcons; // left-hand icons LinearLayout mStatusIcons; @@ -221,7 +223,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mNotificationPanelGravity; int mNotificationPanelMarginBottomPx; float mNotificationPanelMinHeightFrac; - boolean mNotificationPanelIsFullScreenWidth; TextView mNotificationPanelDebugText; // settings @@ -230,7 +231,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, QuickSettingsContainerView mSettingsContainer; // top bar - View mNotificationPanelHeader; + StatusBarHeaderView mHeader; View mKeyguardStatusView; KeyguardBottomAreaView mKeyguardBottomArea; boolean mLeaveOpenOnKeyguardHide; @@ -240,8 +241,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, String mKeyguardHotwordPhrase = ""; int mKeyguardMaxNotificationCount; View mDateTimeView; - View mClearButton; - ImageView mHeaderFlipper; // carrier/wifi label private TextView mCarrierLabel; @@ -249,7 +248,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mCarrierLabelHeight; private TextView mEmergencyCallLabel; private int mStatusBarHeaderHeight; - private View mKeyguardCarrierLabel; private boolean mShowCarrierInPanel = false; @@ -554,8 +552,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( R.id.notification_panel); mNotificationPanel.setStatusBar(this); - mNotificationPanelIsFullScreenWidth = - (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); // make the header non-responsive to clicks mNotificationPanel.findViewById(R.id.header).setOnTouchListener( @@ -609,6 +605,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mPixelFormat = PixelFormat.OPAQUE; mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); + mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); @@ -627,39 +624,30 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); - mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text); mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.addView(mKeyguardIconOverflowContainer); mExpandedContents = mStackScroller; - mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header); + mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); + mHeader.setActivityStarter(this); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text); - mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button); - mClearButton.setOnClickListener(mClearButtonListener); - mClearButton.setAlpha(0f); - mClearButton.setVisibility(View.INVISIBLE); - mClearButton.setEnabled(false); mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); - mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime); + mDateTimeView = mHeader.findViewById(R.id.datetime); if (mDateTimeView != null) { mDateTimeView.setOnClickListener(mClockClickListener); mDateTimeView.setEnabled(true); } - mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper); - - if (!mNotificationPanelIsFullScreenWidth) { - mNotificationPanel.setSystemUiVisibility( - View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | - View.STATUS_BAR_DISABLE_CLOCK); - } + mNotificationPanel.setSystemUiVisibility( + View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | + View.STATUS_BAR_DISABLE_CLOCK); mTicker = new MyTicker(context, mStatusBarView); @@ -680,6 +668,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || QuickSettings.DEBUG_GONE_TILES) { mRotationLockController = new RotationLockController(mContext); } + mUserInfoController = new UserInfoController(mContext); final SignalClusterView signalCluster = (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); @@ -734,11 +723,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mFlipSettingsView = mSettingsContainer; if (mSettingsContainer != null) { mQS = new QuickSettings(mContext, mSettingsContainer); - if (!mNotificationPanelIsFullScreenWidth) { - mSettingsContainer.setSystemUiVisibility( - View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS - | View.STATUS_BAR_DISABLE_SYSTEM_INFO); - } mQS.setService(this); mQS.setBar(mStatusBarView); mQS.setup(mNetworkController, mBluetoothController, mBatteryController, @@ -747,6 +731,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mQS = null; // fly away, be free } + // User info. Trigger first load. + mHeader.setUserInfoController(mUserInfoController); + mUserInfoController.reloadUserInfo(); + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); @@ -1118,19 +1106,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mNavigationBarView != null) { mNavigationBarView.setLayoutDirection(layoutDirection); } - - if (mClearButton != null && mClearButton instanceof ImageView) { - // Force asset reloading - ((ImageView)mClearButton).setImageDrawable(null); - ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear); - } - - if (mHeaderFlipper != null) { - // Force asset reloading - mHeaderFlipper.setImageDrawable(null); - mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings); - } - refreshAllStatusBarIcons(); } @@ -1301,38 +1276,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, + " any=" + any + " clearable=" + clearable); } - if (mFlipSettingsView != null - && mFlipSettingsView.getVisibility() == View.VISIBLE - && mStackScroller.getVisibility() != View.VISIBLE) { - // the flip settings panel is unequivocally showing; we should not be shown - mClearButton.setVisibility(View.INVISIBLE); - } else if (mClearButton.isShown()) { - if (clearable != (mClearButton.getAlpha() == 1.0f)) { - ObjectAnimator clearAnimation = ObjectAnimator.ofFloat( - mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250); - clearAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mClearButton.getAlpha() <= 0.0f) { - mClearButton.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (mClearButton.getAlpha() <= 0.0f) { - mClearButton.setVisibility(View.VISIBLE); - } - } - }); - clearAnimation.start(); - } - } else { - mClearButton.setAlpha(clearable ? 1.0f : 0.0f); - mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE); - } - mClearButton.setEnabled(clearable); - final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); final boolean showDot = (any&&!areLightsOn()); if (showDot != (nlo.getAlpha() == 1.0f)) { @@ -1917,13 +1860,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void checkBarModes() { if (mDemoMode) return; - int sbMode = mStatusBarMode; - if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0 - && mState != StatusBarState.KEYGUARD) { - // if panels are expandable, force the status bar opaque on any interaction - sbMode = MODE_OPAQUE; - } - checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); + checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); if (mNavigationBarView != null) { checkBarMode(mNavigationBarMode, mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); @@ -2449,10 +2386,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final Context context = mContext; final Resources res = context.getResources(); - if (mClearButton instanceof TextView) { - ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button)); - } - // Update the QuickSettings container if (mQS != null) mQS.updateResources(); @@ -2774,20 +2707,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardBottomArea.setVisibility(View.VISIBLE); mKeyguardIndicationTextView.setVisibility(View.VISIBLE); mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); - mKeyguardCarrierLabel.setVisibility(View.VISIBLE); - mNotificationPanelHeader.setVisibility(View.GONE); mNotificationPanel.closeQs(); - mSettingsContainer.setKeyguardShowing(true); } else { mKeyguardStatusView.setVisibility(View.GONE); mKeyguardBottomArea.setVisibility(View.GONE); mKeyguardIndicationTextView.setVisibility(View.GONE); - mKeyguardCarrierLabel.setVisibility(View.GONE); - mNotificationPanelHeader.setVisibility(View.VISIBLE); - - mSettingsContainer.setKeyguardShowing(false); } + mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD); + mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD); updateStackScrollerState(); updatePublicMode(); @@ -2943,4 +2871,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public ViewGroup getQuickSettingsOverlayParent() { return mNotificationPanel; } + + public LinearLayout getSystemIcons() { + return mSystemIcons; + } + + /** + * Reattaches the system icons to its normal parent in collapsed status bar. + */ + public void reattachSystemIcons() { + mSystemIconArea.addView(mSystemIcons, 0); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index e6de057..084bfcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -63,7 +63,7 @@ public class PhoneStatusBarView extends PanelBar { } @Override - public void onAttachedToWindow() { + public void onFinishInflate() { mBarTransitions.init(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java index e1ef83a..005b0d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java @@ -460,6 +460,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, rebindMediaRouterAsCurrentUser(); } }; + mUserTracker.startTracking(); mNextAlarmObserver = new NextAlarmObserver(mHandler); mNextAlarmObserver.startObserving(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 9d33930..5527473 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -17,25 +17,52 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.Intent; +import android.graphics.Outline; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.android.systemui.R; +import com.android.systemui.settings.BrightnessController; +import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.statusbar.policy.UserInfoController; /** * The view to manage the header area in the expanded status bar. */ -public class StatusBarHeaderView extends RelativeLayout { +public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener { private boolean mExpanded; + private boolean mKeyguardShowing; + private View mBackground; - private View mFlipper; + private ViewGroup mSystemIconsContainer; + private View mDateTime; + private View mKeyguardCarrierText; + private MultiUserSwitch mMultiUserSwitch; + private View mDate; + private View mStatusIcons; + private View mSignalCluster; + private View mSettingsButton; + private View mBrightnessContainer; private int mCollapsedHeight; private int mExpandedHeight; + private int mKeyguardHeight; + + private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT; + private int mNormalWidth; + + private ActivityStarter mActivityStarter; + private BrightnessController mBrightnessController; + + private final Rect mClipBounds = new Rect(); + private final Outline mOutline = new Outline(); public StatusBarHeaderView(Context context, AttributeSet attrs) { super(context, attrs); @@ -45,19 +72,35 @@ public class StatusBarHeaderView extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); mBackground = findViewById(R.id.background); - mFlipper = findViewById(R.id.header_flipper); + mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container); + mDateTime = findViewById(R.id.datetime); + mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text); + mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch); + mDate = findViewById(R.id.date); + mSettingsButton = findViewById(R.id.settings_button); + mSettingsButton.setOnClickListener(this); + mBrightnessContainer = findViewById(R.id.brightness_container); + mBrightnessController = new BrightnessController(getContext(), + (ImageView) findViewById(R.id.brightness_icon), + (ToggleSlider) findViewById(R.id.brightness_slider)); loadDimens(); } private void loadDimens() { - mCollapsedHeight = getResources().getDimensionPixelSize( - R.dimen.status_bar_header_height); + mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height); mExpandedHeight = getResources().getDimensionPixelSize( R.dimen.status_bar_header_height_expanded); + mKeyguardHeight = getResources().getDimensionPixelSize( + R.dimen.status_bar_header_height_keyguard); + mNormalWidth = getLayoutParams().width; + } + + public void setActivityStarter(ActivityStarter activityStarter) { + mActivityStarter = activityStarter; } public int getCollapsedHeight() { - return mCollapsedHeight; + return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight; } public int getExpandedHeight() { @@ -65,16 +108,82 @@ public class StatusBarHeaderView extends RelativeLayout { } public void setExpanded(boolean expanded) { - if (expanded != mExpanded) { - ViewGroup.LayoutParams lp = getLayoutParams(); - lp.height = expanded ? mExpandedHeight : mCollapsedHeight; + boolean changed = expanded != mExpanded; + mExpanded = expanded; + if (changed) { + updateHeights(); + updateVisibilities(); + updateSystemIconsLayoutParams(); + updateBrightnessControllerState(); + } + } + + private void updateHeights() { + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; + int height; + if (mExpanded) { + height = mExpandedHeight; + } else if (onKeyguardAndCollapsed) { + height = mKeyguardHeight; + } else { + height = mCollapsedHeight; + } + ViewGroup.LayoutParams lp = getLayoutParams(); + if (lp.height != height) { + lp.height = height; setLayoutParams(lp); - mExpanded = expanded; + } + int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight; + lp = mSystemIconsContainer.getLayoutParams(); + if (lp.height != systemIconsContainerHeight) { + lp.height = systemIconsContainerHeight; + mSystemIconsContainer.setLayoutParams(lp); + } + lp = mMultiUserSwitch.getLayoutParams(); + if (lp.height != systemIconsContainerHeight) { + lp.height = systemIconsContainerHeight; + mMultiUserSwitch.setLayoutParams(lp); } } - public void setExpansionEnabled(boolean enabled) { - mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE); + private void updateWidth() { + int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth; + ViewGroup.LayoutParams lp = getLayoutParams(); + if (width != lp.width) { + lp.width = width; + setLayoutParams(lp); + } + } + + private void updateVisibilities() { + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; + mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE); + mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + if (mStatusIcons != null) { + mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE); + } + if (mSignalCluster != null) { + mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE); + } + } + + private void updateSystemIconsLayoutParams() { + RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams(); + lp.addRule(RelativeLayout.START_OF, mExpanded + ? mSettingsButton.getId() + : mMultiUserSwitch.getId()); + } + + private void updateBrightnessControllerState() { + if (mExpanded) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } } public void setExpansion(float height) { @@ -89,9 +198,65 @@ public class StatusBarHeaderView extends RelativeLayout { } else { mBackground.setTranslationY(0); } + setClipping(height); + } + + private void setClipping(float height) { + mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height); + setClipBounds(mClipBounds); + mOutline.setRect(mClipBounds); + setOutline(mOutline); } public View getBackgroundView() { return mBackground; } + + public void attachSystemIcons(LinearLayout systemIcons) { + mSystemIconsContainer.addView(systemIcons); + mStatusIcons = systemIcons.findViewById(R.id.statusIcons); + mSignalCluster = systemIcons.findViewById(R.id.signal_cluster); + } + + public void onSystemIconsDetached() { + if (mStatusIcons != null) { + mStatusIcons.setVisibility(View.VISIBLE); + } + if (mSignalCluster != null) { + mSignalCluster.setVisibility(View.VISIBLE); + } + mStatusIcons = null; + mSignalCluster = null; + } + + public void setKeyguardShowing(boolean keyguardShowing) { + mKeyguardShowing = keyguardShowing; + if (keyguardShowing) { + setZ(0); + } else { + setTranslationZ(0); + } + updateHeights(); + updateWidth(); + updateVisibilities(); + } + + public void setUserInfoController(UserInfoController userInfoController) { + mMultiUserSwitch.setUserInfoController(userInfoController); + } + + public void setOverlayParent(ViewGroup parent) { + mMultiUserSwitch.setOverlayParent(parent); + } + + @Override + public void onClick(View v) { + if (v == mSettingsButton) { + startSettingsActivity(); + } + } + + private void startSettingsActivity() { + mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index f24c1b6..48c54fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,8 +73,7 @@ public class StatusBarKeyguardViewManager { public void show(Bundle options) { mShowing = true; mStatusBarWindowManager.setKeyguardShowing(true); - showBouncerOrKeyguard(); - updateStates(); + reset(); } /** @@ -105,13 +104,15 @@ public class StatusBarKeyguardViewManager { * Reset the state of the view. */ public void reset() { - if (mOccluded) { - mPhoneStatusBar.hideKeyguard(); - mBouncer.hide(); - } else { - showBouncerOrKeyguard(); + if (mShowing) { + if (mOccluded) { + mPhoneStatusBar.hideKeyguard(); + mBouncer.hide(); + } else { + showBouncerOrKeyguard(); + } + updateStates(); } - updateStates(); } public void onScreenTurnedOff() { @@ -121,7 +122,6 @@ public class StatusBarKeyguardViewManager { public void onScreenTurnedOn(final IKeyguardShowCallback callback) { mScreenOn = true; - reset(); if (callback != null) { callbackAfterDraw(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index a4c9df5..8809d18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -78,9 +78,8 @@ public class StatusBarWindowManager { | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, PixelFormat.TRANSLUCENT); - mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 8ced1c9..55a0bba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.TypedArray; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -30,6 +31,7 @@ import android.util.AttributeSet; import android.widget.TextView; import com.android.systemui.DemoMode; +import com.android.systemui.R; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -52,7 +54,7 @@ public class Clock extends TextView implements DemoMode { private static final int AM_PM_STYLE_SMALL = 1; private static final int AM_PM_STYLE_GONE = 2; - private static final int AM_PM_STYLE = AM_PM_STYLE_GONE; + private final int mAmPmStyle; public Clock(Context context) { this(context, null); @@ -64,6 +66,15 @@ public class Clock extends TextView implements DemoMode { public Clock(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.Clock, + 0, 0); + try { + mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE); + } finally { + a.recycle(); + } } @Override @@ -145,7 +156,7 @@ public class Clock extends TextView implements DemoMode { * add dummy characters around it to let us find it again after * formatting and change its size. */ - if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) { + if (mAmPmStyle != AM_PM_STYLE_NORMAL) { int a = -1; boolean quoted = false; for (int i = 0; i < format.length(); i++) { @@ -177,15 +188,15 @@ public class Clock extends TextView implements DemoMode { } String result = sdf.format(mCalendar.getTime()); - if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) { + if (mAmPmStyle != AM_PM_STYLE_NORMAL) { int magic1 = result.indexOf(MAGIC1); int magic2 = result.indexOf(MAGIC2); if (magic1 >= 0 && magic2 > magic1) { SpannableStringBuilder formatted = new SpannableStringBuilder(result); - if (AM_PM_STYLE == AM_PM_STYLE_GONE) { + if (mAmPmStyle == AM_PM_STYLE_GONE) { formatted.delete(magic1, magic2+1); } else { - if (AM_PM_STYLE == AM_PM_STYLE_SMALL) { + if (mAmPmStyle == AM_PM_STYLE_SMALL) { CharacterStyle style = new RelativeSizeSpan(0.7f); formatted.setSpan(style, magic1, magic2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java new file mode 100644 index 0000000..173af40 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -0,0 +1,215 @@ +/* + * 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 com.android.systemui.statusbar.policy; + +import android.app.ActivityManagerNative; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; +import android.os.AsyncTask; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.ContactsContract; +import android.security.KeyChain; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.view.RotationPolicy; +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; + +public final class UserInfoController { + + private static final String TAG = "UserInfoController"; + + private final Context mContext; + private final ArrayList<OnUserInfoChangedListener> mCallbacks = + new ArrayList<OnUserInfoChangedListener>(); + private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask; + + private boolean mUseDefaultAvatar; + private String mUserName; + private Drawable mUserDrawable; + + public UserInfoController(Context context) { + mContext = context; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + mContext.registerReceiver(mReceiver, filter); + + IntentFilter profileFilter = new IntentFilter(); + profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED); + profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); + mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter, + null, null); + } + + public void addListener(OnUserInfoChangedListener callback) { + mCallbacks.add(callback); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + reloadUserInfo(); + } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { + if (mUseDefaultAvatar) { + reloadUserInfo(); + } + } + } + }; + + private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) || + Intent.ACTION_USER_INFO_CHANGED.equals(action)) { + try { + final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id; + final int changedUser = + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); + if (changedUser == currentUser) { + reloadUserInfo(); + } + } catch (RemoteException e) { + Log.e(TAG, "Couldn't get current user id for profile change", e); + } + } + } + }; + + public void reloadUserInfo() { + if (mUserInfoTask != null) { + mUserInfoTask.cancel(false); + mUserInfoTask = null; + } + queryForUserInformation(); + } + + private Bitmap circularClip(Bitmap input) { + Bitmap output = Bitmap.createBitmap(input.getWidth(), + input.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + final Paint paint = new Paint(); + paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + paint.setAntiAlias(true); + canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, paint); + return output; + } + + private void queryForUserInformation() { + Context currentUserContext; + UserInfo userInfo; + try { + userInfo = ActivityManagerNative.getDefault().getCurrentUser(); + currentUserContext = mContext.createPackageContextAsUser("android", 0, + new UserHandle(userInfo.id)); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Couldn't create user context", e); + throw new RuntimeException(e); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't get user info", e); + throw new RuntimeException(e); + } + final int userId = userInfo.id; + final String userName = userInfo.name; + + final Context context = currentUserContext; + mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { + @Override + protected Pair<String, Drawable> doInBackground(Void... params) { + final UserManager um = UserManager.get(mContext); + + // Fall back to the UserManager nickname if we can't read the name from the local + // profile below. + String name = userName; + Drawable avatar = null; + Bitmap rawAvatar = um.getUserIcon(userId); + if (rawAvatar != null) { + avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar)); + } else { + avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); + mUseDefaultAvatar = true; + } + + // If it's a single-user device, get the profile name, since the nickname is not + // usually valid + if (um.getUsers().size() <= 1) { + // Try and read the display name from the local profile + final Cursor cursor = context.getContentResolver().query( + ContactsContract.Profile.CONTENT_URI, new String[] { + ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME}, + null, null, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + name = cursor.getString(cursor.getColumnIndex( + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); + } + } finally { + cursor.close(); + } + } + } + return new Pair<String, Drawable>(name, avatar); + } + + @Override + protected void onPostExecute(Pair<String, Drawable> result) { + mUserName = result.first; + mUserDrawable = result.second; + mUserInfoTask = null; + notifyChanged(); + } + }; + mUserInfoTask.execute(); + } + + private void notifyChanged() { + for (OnUserInfoChangedListener listener : mCallbacks) { + listener.onUserInfoChanged(mUserName, mUserDrawable); + } + } + + public interface OnUserInfoChangedListener { + public void onUserInfoChanged(String name, Drawable picture); + } +} 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 afd5068..5849afb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -80,9 +80,12 @@ public class NotificationStackScrollLayout extends ViewGroup private Paint mDebugPaint; private int mContentHeight; private int mCollapsedSize; + private int mBottomStackSlowDownHeight; private int mBottomStackPeekSize; private int mEmptyMarginBottom; private int mPaddingBetweenElements; + private int mPaddingBetweenElementsDimmed; + private int mPaddingBetweenElementsNormal; private int mTopPadding; /** @@ -153,7 +156,10 @@ public class NotificationStackScrollLayout extends ViewGroup if (DEBUG) { int y = mCollapsedSize; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); - y = (int) (getLayoutHeight() - mBottomStackPeekSize - mCollapsedSize); + y = (int) (getLayoutHeight() - mBottomStackPeekSize + - mBottomStackSlowDownHeight); + canvas.drawLine(0, y, getWidth(), y, mDebugPaint); + y = (int) (getLayoutHeight() - mBottomStackPeekSize); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); y = (int) getLayoutHeight(); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -183,9 +189,20 @@ public class NotificationStackScrollLayout extends ViewGroup .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); mEmptyMarginBottom = context.getResources().getDimensionPixelSize( R.dimen.notification_stack_margin_bottom); - mPaddingBetweenElements = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding); mStackScrollAlgorithm = new StackScrollAlgorithm(context); + mPaddingBetweenElementsDimmed = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding_dimmed); + mPaddingBetweenElementsNormal = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding); + updatePadding(false); + } + + private void updatePadding(boolean dimmed) { + mPaddingBetweenElements = dimmed + ? mPaddingBetweenElementsDimmed + : mPaddingBetweenElementsNormal; + mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength(); + updateContentHeight(); } @Override @@ -739,15 +756,10 @@ public class NotificationStackScrollLayout extends ViewGroup if (firstChild != null) { int contentHeight = getContentHeight(); int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild); - - scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize); + scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize + + mBottomStackSlowDownHeight); if (scrollRange > 0) { View lastChild = getLastChildNotGone(); - if (isViewExpanded(lastChild)) { - // last child is expanded, so we have to ensure that it can exit the - // bottom stack - scrollRange += mCollapsedSize + mPaddingBetweenElements; - } // We want to at least be able collapse the first item and not ending in a weird // end state. scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize); @@ -1181,7 +1193,7 @@ public class NotificationStackScrollLayout extends ViewGroup public int getEmptyBottomMargin() { int emptyMargin = mMaxLayoutHeight - mContentHeight; if (needsHeightAdaption()) { - emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize; + emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize; } return Math.max(emptyMargin, 0); } @@ -1226,7 +1238,9 @@ public class NotificationStackScrollLayout extends ViewGroup * See {@link AmbientState#setDimmed}. */ public void setDimmed(boolean dimmed, boolean animate) { + mStackScrollAlgorithm.setDimmed(dimmed); mAmbientState.setDimmed(dimmed); + updatePadding(dimmed); if (animate) { mDimmedNeedsAnimation = true; mNeedsAnimation = true; @@ -1311,6 +1325,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_DIMMED new AnimationFilter() + .animateY() .animateScale() .animateDimmed() }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java index 38b544f..1c37c35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java @@ -38,27 +38,26 @@ public class PiecewiseLinearIndentationFunctor extends StackIndentationFunctor { * the actual visual distance below the top card but is a maximum, * achieved when the next card just starts transitioning into the stack and * the stack is full. - * If totalTransitionDistance is equal to this, we directly start at the peek, - * otherwise the first element transitions between 0 and - * totalTransitionDistance - peekSize. + * If distanceToPeekStart is 0, we directly start at the peek, otherwise the + * first element transitions between 0 and distanceToPeekStart. * Visualization: * --------------------------------------------------- --- * | | | - * | FIRST ITEM | | <- totalTransitionDistance + * | FIRST ITEM | | <- distanceToPeekStart * | | | - * |---------------------------------------------------| | --- - * |__________________SECOND ITEM______________________| | | <- peekSize - * |===================================================| _|_ _|_ + * |---------------------------------------------------| --- --- + * |__________________SECOND ITEM______________________| | <- peekSize + * |===================================================| _|_ * - * @param totalTransitionDistance The total transition distance an element has to go through + * @param distanceToPeekStart The distance to the start of the peak. * @param linearPart The interpolation factor between the linear and the quadratic amount taken. * This factor must be somewhere in [0 , 1] */ PiecewiseLinearIndentationFunctor(int maxItemsInStack, int peekSize, - int totalTransitionDistance, + int distanceToPeekStart, float linearPart) { - super(maxItemsInStack, peekSize, totalTransitionDistance); + super(maxItemsInStack, peekSize, distanceToPeekStart); mBaseValues = new ArrayList<Float>(maxItemsInStack+1); initBaseValues(); mLinearPart = linearPart; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java index f72947a..034eba6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java @@ -21,8 +21,8 @@ package com.android.systemui.statusbar.stack; */ public abstract class StackIndentationFunctor { - protected final int mTotalTransitionDistance; - protected final int mDistanceToPeekStart; + protected int mTotalTransitionDistance; + protected int mDistanceToPeekStart; protected int mMaxItemsInStack; protected int mPeekSize; protected boolean mStackStartsAtPeek; @@ -37,31 +37,41 @@ public abstract class StackIndentationFunctor { * the actual visual distance below the top card but is a maximum, * achieved when the next card just starts transitioning into the stack and * the stack is full. - * If totalTransitionDistance is equal to this, we directly start at the peek, - * otherwise the first element transitions between 0 and - * totalTransitionDistance - peekSize. + * If distanceToPeekStart is 0, we directly start at the peek, otherwise the + * first element transitions between 0 and distanceToPeekStart. * Visualization: * --------------------------------------------------- --- * | | | - * | FIRST ITEM | | <- totalTransitionDistance + * | FIRST ITEM | | <- distanceToPeekStart * | | | - * |---------------------------------------------------| | --- - * |__________________SECOND ITEM______________________| | | <- peekSize - * |===================================================| _|_ _|_ + * |---------------------------------------------------| --- --- + * |__________________SECOND ITEM______________________| | <- peekSize + * |===================================================| _|_ * - * @param totalTransitionDistance The total transition distance an element has to go through + * @param distanceToPeekStart The distance to the start of the peak. */ - StackIndentationFunctor(int maxItemsInStack, int peekSize, int totalTransitionDistance) { - mTotalTransitionDistance = totalTransitionDistance; - mDistanceToPeekStart = mTotalTransitionDistance - peekSize; + StackIndentationFunctor(int maxItemsInStack, int peekSize, int distanceToPeekStart) { + mDistanceToPeekStart = distanceToPeekStart; mStackStartsAtPeek = mDistanceToPeekStart == 0; mMaxItemsInStack = maxItemsInStack; mPeekSize = peekSize; + updateTotalTransitionDistance(); } + private void updateTotalTransitionDistance() { + mTotalTransitionDistance = mDistanceToPeekStart + mPeekSize; + } + public void setPeekSize(int mPeekSize) { this.mPeekSize = mPeekSize; + updateTotalTransitionDistance(); + } + + public void setDistanceToPeekStart(int distanceToPeekStart) { + mDistanceToPeekStart = distanceToPeekStart; + mStackStartsAtPeek = mDistanceToPeekStart == 0; + updateTotalTransitionDistance(); } /** 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 5e4d496..bd9de82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -65,13 +65,40 @@ public class StackScrollAlgorithm { private ExpandableView mFirstChildWhileExpanding; private boolean mExpandedOnStart; private int mTopStackTotalSize; + private int mPaddingBetweenElementsDimmed; + private int mPaddingBetweenElementsNormal; + private int mBottomStackSlowDownLength; public StackScrollAlgorithm(Context context) { initConstants(context); + updatePadding(false); + } + + private void updatePadding(boolean dimmed) { + mPaddingBetweenElements = dimmed + ? mPaddingBetweenElementsDimmed + : mPaddingBetweenElementsNormal; + mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements; + mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( + MAX_ITEMS_IN_TOP_STACK, + mTopStackPeekSize, + mTopStackTotalSize - mTopStackPeekSize, + 0.5f); + mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( + MAX_ITEMS_IN_BOTTOM_STACK, + mBottomStackPeekSize, + getBottomStackSlowDownLength(), + 0.5f); + } + + public int getBottomStackSlowDownLength() { + return mBottomStackSlowDownLength + mPaddingBetweenElements; } private void initConstants(Context context) { - mPaddingBetweenElements = context.getResources() + mPaddingBetweenElementsDimmed = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding_dimmed); + mPaddingBetweenElementsNormal = context.getResources() .getDimensionPixelSize(R.dimen.notification_padding); mCollapsedSize = context.getResources() .getDimensionPixelSize(R.dimen.notification_min_height); @@ -82,17 +109,8 @@ public class StackScrollAlgorithm { mZDistanceBetweenElements = context.getResources() .getDimensionPixelSize(R.dimen.z_distance_between_notifications); mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; - mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements; - mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( - MAX_ITEMS_IN_TOP_STACK, - mTopStackPeekSize, - mTopStackTotalSize, - 0.5f); - mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( - MAX_ITEMS_IN_BOTTOM_STACK, - mBottomStackPeekSize, - mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements, - 0.5f); + mBottomStackSlowDownLength = context.getResources() + .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); } @@ -206,7 +224,7 @@ public class StackScrollAlgorithm { float bottomPeekStart = mInnerHeight - mBottomStackPeekSize; // The position where the bottom stack starts. - float bottomStackStart = bottomPeekStart - mCollapsedSize; + float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength; // The y coordinate of the current child. float currentYPosition = 0.0f; @@ -352,7 +370,7 @@ public class StackScrollAlgorithm { algorithmState.itemsInBottomStack += algorithmState.partialInBottom; childViewState.yTranslation = transitioningPositionStart + offset - childHeight - mPaddingBetweenElements; - + // We want at least to be at the end of the top stack when collapsing clampPositionToTopStackEnd(childViewState, childHeight); childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; @@ -621,6 +639,10 @@ public class StackScrollAlgorithm { } } + public void setDimmed(boolean dimmed) { + updatePadding(dimmed); + } + class StackScrollAlgorithmState { /** diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 3619112..4a59a8f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -38,6 +38,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Point; import android.graphics.Rect; @@ -197,6 +198,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final SparseArray<UserState> mUserStates = new SparseArray<UserState>(); + private final UserManager mUserManager; + private int mCurrentUserId = UserHandle.USER_OWNER; private final LongArray mTempLongArray = new LongArray(); @@ -210,15 +213,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return getUserStateLocked(mCurrentUserId); } - private UserState getUserStateLocked(int userId) { - UserState state = mUserStates.get(userId); - if (state == null) { - state = new UserState(userId); - mUserStates.put(userId, state); - } - return state; - } - /** * Creates a new instance. * @@ -228,6 +222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mContext = context; mPackageManager = mContext.getPackageManager(); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mSecurityPolicy = new SecurityPolicy(); mMainHandler = new MainHandler(mContext.getMainLooper()); registerBroadcastReceivers(); @@ -235,11 +230,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { context.getContentResolver()); } + private UserState getUserStateLocked(int userId) { + UserState state = mUserStates.get(userId); + if (state == null) { + state = new UserState(userId); + mUserStates.put(userId, state); + } + return state; + } + private void registerBroadcastReceivers() { PackageMonitor monitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { synchronized (mLock) { + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. if (getChangingUserId() != mCurrentUserId) { return; } @@ -262,6 +268,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { final int userId = getChangingUserId(); + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. if (userId != mCurrentUserId) { return; } @@ -297,6 +305,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { int uid, boolean doit) { synchronized (mLock) { final int userId = getChangingUserId(); + // Only the profile parent can install accessibility services. + // Therefore we ignore packages from linked profiles. if (userId != mCurrentUserId) { return false; } @@ -359,6 +369,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public int addClient(IAccessibilityManagerClient client, int userId) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); // If the client is from a process that runs across users such as @@ -388,6 +401,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution.. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); // This method does nothing for a background user. @@ -414,6 +430,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); // The automation service is a fake one and should not be reported @@ -435,6 +454,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { int userId) { List<AccessibilityServiceInfo> result = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); @@ -468,6 +490,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void interrupt(int userId) { CopyOnWriteArrayList<Service> services; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); // This method does nothing for a background user. @@ -491,6 +516,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public int addAccessibilityInteractionConnection(IWindow windowToken, IAccessibilityInteractionConnection connection, int userId) throws RemoteException { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); final int windowId = sNextWindowId++; @@ -527,6 +555,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public void removeAccessibilityInteractionConnection(IWindow window) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); IBinder token = window.asBinder(); @@ -675,6 +706,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Manifest.permission.RETRIEVE_WINDOW_TOKEN, GET_WINDOW_TOKEN); synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); @@ -770,7 +804,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } // Disconnect from services for the old user. - UserState oldUserState = getUserStateLocked(mCurrentUserId); + UserState oldUserState = getCurrentUserStateLocked(); oldUserState.onSwitchToAnotherUser(); // Disable the local managers for the old user. @@ -2034,6 +2068,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public List<AccessibilityWindowInfo> getWindows() { synchronized (mLock) { + // We treat calls from a profile as if made by its perent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); @@ -2062,6 +2099,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public AccessibilityWindowInfo getWindow(int windowId) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); @@ -2092,6 +2132,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); @@ -2136,9 +2179,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -2180,9 +2226,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -2224,9 +2273,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -2268,9 +2320,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -2311,9 +2366,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -2346,9 +2404,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public boolean performGlobalAction(int action) { synchronized (mLock) { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.getCallingUserId()); + UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { return false; } @@ -3407,16 +3468,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; } + private int resolveProfileParentLocked(int userId) { + if (userId != mCurrentUserId) { + final long identity = Binder.clearCallingIdentity(); + try { + UserInfo parent = mUserManager.getProfileParent(userId); + if (parent != null) { + return parent.getUserHandle().getIdentifier(); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return userId; + } + public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) { final int callingUid = Binder.getCallingUid(); if (callingUid == 0 || callingUid == Process.SYSTEM_UID || callingUid == Process.SHELL_UID) { - return mCurrentUserId; + if (userId == UserHandle.USER_CURRENT + || userId == UserHandle.USER_CURRENT_OR_SELF) { + return mCurrentUserId; + } + return resolveProfileParentLocked(userId); } final int callingUserId = UserHandle.getUserId(callingUid); if (callingUserId == userId) { - return userId; + return resolveProfileParentLocked(userId); } if (!hasPermission(Manifest.permission.INTERACT_ACROSS_USERS) && !hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) { @@ -3673,8 +3753,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void onChange(boolean selfChange, Uri uri) { if (mAccessibilityEnabledUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readAccessibilityEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); @@ -3683,8 +3765,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } else if (mTouchExplorationEnabledUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readTouchExplorationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); @@ -3693,8 +3777,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } else if (mDisplayMagnificationEnabledUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readDisplayMagnificationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); @@ -3703,8 +3789,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } else if (mEnabledAccessibilityServicesUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readEnabledAccessibilityServicesLocked(userState)) { onUserStateChangedLocked(userState); @@ -3713,8 +3801,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) { onUserStateChangedLocked(userState); @@ -3723,8 +3813,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } else if (mEnhancedWebAccessibilityUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readEnhancedWebAccessibilityEnabledChangedLocked(userState)) { onUserStateChangedLocked(userState); @@ -3739,8 +3831,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { || mDisplayInversionUri.equals(uri) || mDisplayDaltonizerUri.equals(uri)) { synchronized (mLock) { - // We will update when the automation service dies. + // Profiles share the accessibility state of the parent. Therefore, + // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); + // We will update when the automation service dies. if (userState.mUiAutomationService == null) { if (readDisplayColorAdjustmentSettingsLocked(userState)) { updateDisplayColorAdjustmentSettingsLocked(userState); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index dfffa8a..4ea33db 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -65,6 +65,7 @@ import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; import android.net.LinkQualityInfo; import android.net.MobileDataStateTracker; +import android.net.Network; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -165,6 +166,8 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; +import static android.net.ConnectivityManager.INVALID_NET_ID; + /** * @hide */ @@ -442,6 +445,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { TelephonyManager mTelephonyManager; + private final static int MIN_NET_ID = 10; // some reserved marks + private final static int MAX_NET_ID = 65535; + private int mNextNetId = MIN_NET_ID; + public ConnectivityService(Context context, INetworkManagementService netd, INetworkStatsService statsService, INetworkPolicyManager policyManager) { // Currently, omitting a NetworkFactory will create one internally @@ -706,6 +713,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); } + private synchronized int nextNetId() { + int netId = mNextNetId; + if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID; + return netId; + } + /** * Factory that creates {@link NetworkStateTracker} instances using given * {@link NetworkConfig}. @@ -1984,6 +1997,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int prevNetType = info.getType(); mNetTrackers[prevNetType].setTeardownRequested(false); + int thisNetId = mNetTrackers[prevNetType].getNetwork().netId; // Remove idletimer previously setup in {@code handleConnect} if (mNetConfigs[prevNetType].isDefault()) { @@ -2069,6 +2083,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(), getConnectivityChangeDelay()); } + try { + mNetd.removeNetwork(thisNetId); + } catch (Exception e) { + loge("Exception removing network: " + e); + } finally { + mNetTrackers[prevNetType].setNetId(INVALID_NET_ID); + } } private void tryFailover(int prevNetType) { @@ -2336,17 +2357,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (mNetConfigs[newNetType].isDefault()) { if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) { if (isNewNetTypePreferredOverCurrentNetType(newNetType)) { - // tear down the other - NetworkStateTracker otherNet = - mNetTrackers[mActiveDefaultNetwork]; - if (DBG) { - log("Policy requires " + otherNet.getNetworkInfo().getTypeName() + - " teardown"); - } - if (!teardown(otherNet)) { - loge("Network declined teardown request"); - teardown(thisNet); - return; + String teardownPolicy = SystemProperties.get("net.teardownPolicy"); + if (TextUtils.equals(teardownPolicy, "keep") == false) { + // tear down the other + NetworkStateTracker otherNet = + mNetTrackers[mActiveDefaultNetwork]; + if (DBG) { + log("Policy requires " + otherNet.getNetworkInfo().getTypeName() + + " teardown"); + } + if (!teardown(otherNet)) { + loge("Network declined teardown request"); + teardown(thisNet); + return; + } + } else { + //TODO - remove + loge("network teardown skipped due to net.teardownPolicy setting"); } } else { // don't accept this one @@ -2358,6 +2385,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } } + int thisNetId = nextNetId(); + thisNet.setNetId(thisNetId); + try { + mNetd.createNetwork(thisNetId, thisIface); + } catch (Exception e) { + loge("Exception creating network :" + e); + teardown(thisNet); + return; + } setupDataActivityTracking(newNetType); synchronized (ConnectivityService.this) { // have a new default network, release the transition wakelock in a second @@ -2380,6 +2416,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Don't do this - if we never sign in stay, grey //reportNetworkCondition(mActiveDefaultNetwork, 100); updateNetworkSettings(thisNet); + } else { + int thisNetId = nextNetId(); + thisNet.setNetId(thisNetId); + try { + mNetd.createNetwork(thisNetId, thisIface); + } catch (Exception e) { + loge("Exception creating network :" + e); + teardown(thisNet); + return; + } } thisNet.setTeardownRequested(false); updateMtuSizeSettings(thisNet); diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java index 0d1e122..96f9ab0 100644 --- a/services/core/java/com/android/server/NativeDaemonConnector.java +++ b/services/core/java/com/android/server/NativeDaemonConnector.java @@ -50,6 +50,8 @@ import java.util.LinkedList; final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { private static final boolean LOGD = false; + private final static boolean VDBG = false; + private final String TAG; private String mSocket; @@ -409,7 +411,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo loge("timed-out waiting for response to " + logCmd); throw new NativeDaemonFailureException(logCmd, event); } - log("RMV <- {" + event + "}"); + if (VDBG) log("RMV <- {" + event + "}"); events.add(event); } while (event.isClassContinue()); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 7ce45f7..b9c86dc 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2029,4 +2029,24 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } + + public void createNetwork(int netId, String iface) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "create", netId, iface); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + public void removeNetwork(int netId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "destroy", netId); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 4f0c9b5..512ebc6 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -171,6 +171,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer); + writer.flush(); for (INetworkScoreCache scoreCache : getScoreCaches()) { try { diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 132ca00..82c13e0 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -41,6 +41,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.view.InputDevice; +import android.media.AudioManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -73,6 +74,8 @@ public class VibratorService extends IVibratorService.Stub private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators private int mCurVibUid = -1; + private boolean mLowPowerMode; + private SettingsObserver mSettingObserver; native static boolean vibratorExists(); native static void vibratorOn(long milliseconds); @@ -159,15 +162,15 @@ public class VibratorService extends IVibratorService.Stub public void systemReady() { mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE); + mSettingObserver = new SettingsObserver(mH); mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true, - new ContentObserver(mH) { - @Override - public void onChange(boolean selfChange) { - updateInputDeviceVibrators(); - } - }, UserHandle.USER_ALL); + Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), + true, mSettingObserver, UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), false, + mSettingObserver, UserHandle.USER_ALL); mContext.registerReceiver(new BroadcastReceiver() { @Override @@ -179,6 +182,17 @@ public class VibratorService extends IVibratorService.Stub updateInputDeviceVibrators(); } + private final class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean SelfChange) { + updateInputDeviceVibrators(); + } + } + public boolean hasVibrator() { return doVibratorExists(); } @@ -346,6 +360,10 @@ public class VibratorService extends IVibratorService.Stub // Lock held on mVibrations private void startVibrationLocked(final Vibration vib) { try { + if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING) { + return; + } + int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, vib.mStreamHint, vib.mUid, vib.mOpPkg); if (mode == AppOpsManager.MODE_ALLOWED) { @@ -425,6 +443,9 @@ public class VibratorService extends IVibratorService.Stub } catch (SettingNotFoundException snfe) { } + mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE, 0) != 0; + if (mVibrateInputDevicesSetting) { if (!mInputDeviceListenerRegistered) { mInputDeviceListenerRegistered = true; diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index 50cfe48..c32beda 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -70,6 +70,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final String NAME_HDMI = "hdmi"; private static final int MSG_NEW_DEVICE_STATE = 1; + private static final int MSG_SYSTEM_READY = 2; private final Object mLock = new Object(); @@ -96,19 +97,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); mObserver = new WiredAccessoryObserver(); - - IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context ctx, Intent intent) { - bootCompleted(); - } - }, - filter, null, null); } - private void bootCompleted() { + private void onSystemReady() { if (mUseDevInputEventForAudioJack) { int switchValues = 0; if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { @@ -159,6 +150,16 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } } + @Override + public void systemReady() { + synchronized (mLock) { + mWakeLock.acquire(); + + Message msg = mHandler.obtainMessage(MSG_SYSTEM_READY, 0, 0, null); + mHandler.sendMessage(msg); + } + } + /** * Compare the existing headset state with the new state and pass along accordingly. Note * that this only supports a single headset at a time. Inserting both a usb and jacked headset @@ -220,6 +221,11 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { case MSG_NEW_DEVICE_STATE: setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); mWakeLock.release(); + break; + case MSG_SYSTEM_READY: + onSystemReady(); + mWakeLock.release(); + break; } } }; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index efc5606..5358c1a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -35,6 +35,7 @@ import android.app.IActivityContainerCallback; import android.appwidget.AppWidgetManager; import android.graphics.Rect; import android.os.BatteryStats; +import android.os.PersistableBundle; import android.service.voice.IVoiceInteractionSession; import android.util.ArrayMap; @@ -1327,12 +1328,14 @@ public final class ActivityManagerService extends ActivityManagerNative String host = ""; String port = ""; String exclList = ""; - String pacFileUrl = null; + String pacFileUrl = ""; if (proxy != null) { host = proxy.getHost(); port = Integer.toString(proxy.getPort()); exclList = proxy.getExclusionListAsString(); - pacFileUrl = proxy.getPacFileUrl().toString(); + if (proxy.getPacFileUrl() != null) { + pacFileUrl = proxy.getPacFileUrl().toString(); + } } synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { @@ -5427,22 +5430,21 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public final void activityPaused(IBinder token) { + public final void activityPaused(IBinder token, PersistableBundle persistentState) { final long origId = Binder.clearCallingIdentity(); synchronized(this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { - stack.activityPausedLocked(token, false); + stack.activityPausedLocked(token, false, persistentState); } } Binder.restoreCallingIdentity(origId); } @Override - public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail, - CharSequence description) { - if (localLOGV) Slog.v( - TAG, "Activity stopped: token=" + token); + public final void activityStopped(IBinder token, Bundle icicle, + PersistableBundle persistentState, CharSequence description) { + if (localLOGV) Slog.v(TAG, "Activity stopped: token=" + token); // Refuse possible leaked file descriptors if (icicle != null && icicle.hasFileDescriptors()) { @@ -5454,7 +5456,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { - r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description); + r.task.stack.activityStoppedLocked(r, icicle, persistentState, description); } } @@ -9570,6 +9572,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mAppOpsService.systemReady(); + mUsageStatsService.systemReady(); mSystemReady = true; } @@ -14391,6 +14394,7 @@ public final class ActivityManagerService extends ActivityManagerNative newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig); + mUsageStatsService.noteStartConfig(newConfig); final Configuration configCopy = new Configuration(mConfiguration); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index efd2b57..8391f79 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.os.PersistableBundle; import android.os.Trace; import com.android.internal.app.ResolverActivity; import com.android.server.AttributeCache; @@ -117,6 +118,7 @@ final class ActivityRecord { ProcessRecord app; // if non-null, hosting application ActivityState state; // current state we are in Bundle icicle; // last saved activity state + PersistableBundle persistentState; // last persistently saved activity state boolean frontOfTask; // is this the root activity of its task? boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 442da31..7c29d85 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -68,6 +68,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -276,7 +277,7 @@ final class ActivityStack { if (r.app != null) { mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r); } - activityPausedLocked(r.appToken, true); + activityPausedLocked(r.appToken, true, r.persistentState); } } break; case LAUNCH_TICK_MSG: { @@ -860,13 +861,15 @@ final class ActivityStack { } } - final void activityPausedLocked(IBinder token, boolean timeout) { + final void activityPausedLocked(IBinder token, boolean timeout, + PersistableBundle persistentState) { if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", timeout=" + timeout); final ActivityRecord r = isInStackLocked(token); if (r != null) { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); + r.persistentState = persistentState; if (mPausingActivity == r) { if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r + (timeout ? " (due to timeout)" : " (pause complete)")); @@ -881,13 +884,14 @@ final class ActivityStack { } } - final void activityStoppedLocked(ActivityRecord r, Bundle icicle, Bitmap thumbnail, - CharSequence description) { + final void activityStoppedLocked(ActivityRecord r, Bundle icicle, + PersistableBundle persistentState, CharSequence description) { if (r.state != ActivityState.STOPPING) { Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r); mHandler.removeMessages(STOP_TIMEOUT_MSG, r); return; } + r.persistentState = persistentState; if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle); if (icicle != null) { // If icicle is null, this is happening due to a timeout, so we @@ -895,7 +899,7 @@ final class ActivityStack { r.icicle = icicle; r.haveState = true; r.launchCount = 0; - r.updateThumbnail(thumbnail, description); + r.updateThumbnail(null, description); } if (!r.stopped) { if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)"); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ce3d853..6f62a03 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1026,10 +1026,10 @@ public final class ActivityStackSupervisor implements DisplayListener { r.clearOptionsLocked(); app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, - new Configuration(mService.mConfiguration), r.compat, - r.task.voiceInteractor, app.repProcState, r.icicle, results, newIntents, - !andResume, mService.isNextTransitionForward(), profileFile, profileFd, - profileAutoStop, options); + new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor, + app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, + mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop, + options); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { // This may be a heavy-weight process! Note that the package diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java index 42cf900..4a5a554 100644 --- a/services/core/java/com/android/server/am/UsageStatsService.java +++ b/services/core/java/com/android/server/am/UsageStatsService.java @@ -17,26 +17,31 @@ package com.android.server.am; import android.app.AppGlobals; +import android.app.AppOpsManager; +import android.app.UsageStats; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.os.Binder; import android.os.IBinder; import android.os.FileUtils; import android.os.Parcel; +import android.os.ParcelableParcel; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Slog; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.app.IUsageStats; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.PkgUsageStats; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -46,7 +51,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -55,8 +59,6 @@ import java.util.Calendar; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -75,7 +77,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1008; + private static final int VERSION = 1010; private static final int CHECKIN_VERSION = 4; @@ -94,13 +96,10 @@ public final class UsageStatsService extends IUsageStats.Stub { static IUsageStats sService; private Context mContext; - // structure used to maintain statistics since the last checkin. - final private ArrayMap<String, PkgUsageStatsExtended> mStats - = new ArrayMap<String, PkgUsageStatsExtended>(); + private AppOpsManager mAppOps; - // Maintains the last time any component was resumed, for all time. - final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes - = new ArrayMap<String, ArrayMap<String, Long>>(); + // structure used to maintain statistics since the last checkin. + private LocalUsageStats mStats = new LocalUsageStats(); // To remove last-resume time stats when a pacakge is removed. private PackageMonitor mPackageMonitor; @@ -115,6 +114,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private String mLastResumedPkg; private String mLastResumedComp; private boolean mIsResumed; + private ConfigUsageStatsExtended mCurrentConfigStats; private File mFile; private AtomicFile mHistoryFile; private String mFileLeaf; @@ -127,6 +127,30 @@ public final class UsageStatsService extends IUsageStats.Stub { private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0); private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false); + static class LocalUsageStats extends UsageStats { + public LocalUsageStats() { + } + public LocalUsageStats(Parcel in, boolean extended) { + super(in, extended); + } + @Override + public PackageStats onNewPackageStats(String pkgName) { + return new PkgUsageStatsExtended(pkgName); + } + @Override + public PackageStats onNewPackageStats(Parcel in) { + return new PkgUsageStatsExtended(in); + } + @Override + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigUsageStatsExtended(config); + } + @Override + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigUsageStatsExtended(source); + } + } + static class TimeStats { int mCount; final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS]; @@ -166,27 +190,18 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - static class PkgUsageStatsExtended { + static class PkgUsageStatsExtended extends UsageStats.PackageStats { final ArrayMap<String, TimeStats> mLaunchTimes = new ArrayMap<String, TimeStats>(); final ArrayMap<String, TimeStats> mFullyDrawnTimes = new ArrayMap<String, TimeStats>(); - int mLaunchCount; - long mUsageTime; - long mPausedTime; - long mResumedTime; - PkgUsageStatsExtended() { - mLaunchCount = 0; - mUsageTime = 0; + PkgUsageStatsExtended(String pkgName) { + super(pkgName); } PkgUsageStatsExtended(Parcel in) { - mLaunchCount = in.readInt(); - mUsageTime = in.readLong(); - if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount - + ", Usage time:" + mUsageTime); - + super(in); final int numLaunchTimeStats = in.readInt(); if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); mLaunchTimes.ensureCapacity(numLaunchTimeStats); @@ -208,18 +223,6 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume(String comp, boolean launched) { - if (launched) { - mLaunchCount++; - } - mResumedTime = SystemClock.elapsedRealtime(); - } - - void updatePause() { - mPausedTime = SystemClock.elapsedRealtime(); - mUsageTime += (mPausedTime - mResumedTime); - } - void addLaunchCount(String comp) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -247,9 +250,7 @@ public final class UsageStatsService extends IUsageStats.Stub { times.add(millis); } - void writeToParcel(Parcel out) { - out.writeInt(mLaunchCount); - out.writeLong(mUsageTime); + public void writeExtendedToParcel(Parcel out, int parcelableFlags) { final int numLaunchTimeStats = mLaunchTimes.size(); out.writeInt(numLaunchTimeStats); for (int i=0; i<numLaunchTimeStats; i++) { @@ -264,11 +265,21 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void clear() { + @Override + public boolean clearUsageTimes() { mLaunchTimes.clear(); mFullyDrawnTimes.clear(); - mLaunchCount = 0; - mUsageTime = 0; + return super.clearUsageTimes(); + } + } + + static class ConfigUsageStatsExtended extends UsageStats.ConfigurationStats { + ConfigUsageStatsExtended(Configuration config) { + super(config); + } + + ConfigUsageStatsExtended(Parcel in) { + super(in); } } @@ -364,18 +375,9 @@ public final class UsageStatsService extends IUsageStats.Stub { + VERSION + "; dropping"); return; } - int N = in.readInt(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } - if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - synchronized (mStatsLock) { - mStats.put(pkgName, pus); - } + LocalUsageStats stats = new LocalUsageStats(in, true); + synchronized (mStatsLock) { + mStats = stats; } } @@ -419,12 +421,9 @@ public final class UsageStatsService extends IUsageStats.Stub { try { long lastResumeTime = Long.parseLong(lastResumeTimeStr); synchronized (mStatsLock) { - ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg); - if (lrt == null) { - lrt = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkg, lrt); - } - lrt.put(comp, lastResumeTime); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkg); + pus.componentResumeTimes.put(comp, lastResumeTime); } } catch (NumberFormatException e) { } @@ -543,6 +542,15 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } + Parcel out = Parcel.obtain(); + synchronized (mStatsLock) { + out.writeInt(VERSION); + mStats.writeExtendedToParcel(out, 0); + if (dayChanged) { + mStats.clearUsageTimes(); + } + } + synchronized (mFileLock) { // Get the most recent file mFileLeaf = getCurrentDateStr(FILE_PREFIX); @@ -553,6 +561,7 @@ public final class UsageStatsService extends IUsageStats.Stub { if (!backupFile.exists()) { if (!mFile.renameTo(backupFile)) { Slog.w(TAG, "Failed to persist new stats"); + out.recycle(); return; } } else { @@ -562,14 +571,10 @@ public final class UsageStatsService extends IUsageStats.Stub { try { // Write mStats to file - writeStatsFLOCK(mFile); + writeStatsFLOCK(mFile, out); mLastWriteElapsedTime.set(currElapsedTime); if (dayChanged) { mLastWriteDay.set(curDay); - // clear stats - synchronized (mStats) { - mStats.clear(); - } mFile = new File(mDir, mFileLeaf); checkFileLimitFLOCK(); } @@ -590,17 +595,15 @@ public final class UsageStatsService extends IUsageStats.Stub { backupFile.renameTo(mFile); } } + out.recycle(); } if (localLOGV) Slog.d(TAG, "Dumped usage stats."); } - private void writeStatsFLOCK(File file) throws IOException { + private void writeStatsFLOCK(File file, Parcel parcel) throws IOException { FileOutputStream stream = new FileOutputStream(file); try { - Parcel out = Parcel.obtain(); - writeStatsToParcelFLOCK(out); - stream.write(out.marshall()); - out.recycle(); + stream.write(parcel.marshall()); stream.flush(); } finally { FileUtils.sync(stream); @@ -608,29 +611,14 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - private void writeStatsToParcelFLOCK(Parcel out) { - synchronized (mStatsLock) { - out.writeInt(VERSION); - Set<String> keys = mStats.keySet(); - out.writeInt(keys.size()); - for (String key : keys) { - PkgUsageStatsExtended pus = mStats.get(key); - out.writeString(key); - pus.writeToParcel(out); - } - } - } - /** Filter out stats for any packages which aren't present anymore. */ private void filterHistoryStats() { synchronized (mStatsLock) { IPackageManager pm = AppGlobals.getPackageManager(); - for (int i=0; i<mLastResumeTimes.size(); i++) { - String pkg = mLastResumeTimes.keyAt(i); + for (int i=mStats.mPackages.size()-1; i>=0; i--) { try { - if (pm.getPackageUid(pkg, 0) < 0) { - mLastResumeTimes.removeAt(i); - i--; + if (pm.getPackageUid(mStats.mPackages.valueAt(i).getPackageName(), 0) < 0) { + mStats.mPackages.removeAt(i); } } catch (RemoteException e) { } @@ -648,10 +636,12 @@ public final class UsageStatsService extends IUsageStats.Stub { out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "usage-history"); synchronized (mStatsLock) { - for (int i=0; i<mLastResumeTimes.size(); i++) { + int NP = mStats.mPackages.size(); + for (int i=0; i<NP; i++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(i); out.startTag(null, "pkg"); - out.attribute(null, "name", mLastResumeTimes.keyAt(i)); - ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i); + out.attribute(null, "name", ps.getPackageName()); + ArrayMap<String, Long> comp = ps.componentResumeTimes; for (int j=0; j<comp.size(); j++) { out.startTag(null, "comp"); out.attribute(null, "name", comp.keyAt(j)); @@ -678,6 +668,10 @@ public final class UsageStatsService extends IUsageStats.Stub { ServiceManager.addService(SERVICE_NAME, asBinder()); } + public void systemReady() { + mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); + } + /** * Start watching packages to remove stats when a package is uninstalled. * May only be called when the package manager is ready. @@ -687,7 +681,7 @@ public final class UsageStatsService extends IUsageStats.Stub { @Override public void onPackageRemovedAllUsers(String packageName, int uid) { synchronized (mStatsLock) { - mLastResumeTimes.remove(packageName); + mStats.mPackages.remove(packageName); } } }; @@ -729,9 +723,10 @@ public final class UsageStatsService extends IUsageStats.Stub { // to recover. if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName + " while already resumed in " + mLastResumedPkg); - PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats( + mLastResumedPkg); if (pus != null) { - pus.updatePause(); + pus.pause(); } } } @@ -744,22 +739,13 @@ public final class UsageStatsService extends IUsageStats.Stub { mLastResumedComp = componentName.getClassName(); if (localLOGV) Slog.i(TAG, "started component:" + pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - pus = new PkgUsageStatsExtended(); - mStats.put(pkgName, pus); - } - pus.updateResume(mLastResumedComp, !samePackage); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkgName); + pus.resume(!samePackage); if (!sameComp) { pus.addLaunchCount(mLastResumedComp); } - - ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName); - if (componentResumeTimes == null) { - componentResumeTimes = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkgName, componentResumeTimes); - } - componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); + pus.componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); } } @@ -782,13 +768,13 @@ public final class UsageStatsService extends IUsageStats.Stub { if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus == null) { // Weird some error here Slog.i(TAG, "No package stats for pkg:"+pkgName); return; } - pus.updatePause(); + pus.pause(); } // Persist current data to file if needed. @@ -808,7 +794,7 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addLaunchTime(componentName.getClassName(), millis); } @@ -827,13 +813,29 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addFullyDrawnTime(componentName.getClassName(), millis); } } } + public void noteStartConfig(Configuration config) { + enforceCallingPermission(); + synchronized (mStatsLock) { + config = new Configuration(config); + ConfigUsageStatsExtended cus = (ConfigUsageStatsExtended) + mStats.getOrCreateConfigurationStats(config); + if (cus != mCurrentConfigStats) { + if (mCurrentConfigStats != null) { + mCurrentConfigStats.stop(); + } + cus.start(); + mCurrentConfigStats = cus; + } + } + } + public void enforceCallingPermission() { if (Binder.getCallingPid() == Process.myPid()) { return; @@ -843,53 +845,71 @@ public final class UsageStatsService extends IUsageStats.Stub { } @Override - public PkgUsageStats getPkgUsageStats(ComponentName componentName) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats getPkgUsageStats(String callingPkg, + ComponentName componentName) { + checkCallerPermission(callingPkg, "getPkgUsageStats"); String pkgName; if ((componentName == null) || ((pkgName = componentName.getPackageName()) == null)) { return null; } synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); - Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName); - if (pus == null && lastResumeTimes == null) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); + if (pus == null) { return null; } - int launchCount = pus != null ? pus.mLaunchCount : 0; - long usageTime = pus != null ? pus.mUsageTime : 0; - return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes); + return new UsageStats.PackageStats(pus); } } @Override - public PkgUsageStats[] getAllPkgUsageStats() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg) { + checkCallerPermission(callingPkg, "getAllPkgUsageStats"); synchronized (mStatsLock) { - int size = mLastResumeTimes.size(); - if (size <= 0) { + int NP = mStats.mPackages.size(); + if (NP <= 0) { return null; } - PkgUsageStats retArr[] = new PkgUsageStats[size]; - for (int i=0; i<size; i++) { - String pkg = mLastResumeTimes.keyAt(i); - long usageTime = 0; - int launchCount = 0; - - PkgUsageStatsExtended pus = mStats.get(pkg); - if (pus != null) { - usageTime = pus.mUsageTime; - launchCount = pus.mLaunchCount; - } - retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, - mLastResumeTimes.valueAt(i)); + UsageStats.PackageStats retArr[] = new UsageStats.PackageStats[NP]; + for (int p=0; p<NP; p++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(p); + retArr[p] = new UsageStats.PackageStats(ps); } return retArr; } } + @Override + public ParcelableParcel getCurrentStats(String callingPkg) { + checkCallerPermission(callingPkg, "getCurrentStats"); + synchronized (mStatsLock) { + ParcelableParcel out = new ParcelableParcel(null); + mStats.writeToParcel(out.getParcel(), 0); + return out; + } + } + + private void checkCallerPermission(String callingPkg, String callingOp) { + // Because the permission for this is system-only, its use with + // app ops is a little different: the op is disabled by default, + // and enabling it allows apps to get access even if they don't + // hold the permission. + int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(), + callingPkg); + if (mode == AppOpsManager.MODE_ALLOWED) { + return; + } else if (mode != AppOpsManager.MODE_IGNORED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS) + == PackageManager.PERMISSION_GRANTED) { + return; + } + } + + String msg = "Package " + callingPkg + " not allowed to call " + callingOp; + throw new SecurityException(msg); + } + static byte[] readFully(FileInputStream stream) throws IOException { int pos = 0; int avail = stream.available(); @@ -963,31 +983,28 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } - pw.println(sb.toString()); - int N = in.readInt(); + final LocalUsageStats stats = new LocalUsageStats(in, true); + final long time = SystemClock.elapsedRealtime(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } + pw.println(sb.toString()); + int NP = stats.mPackages.size(); + for (int p=0; p<NP; p++) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)stats.mPackages.valueAt(p); sb.setLength(0); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - if (packages != null && !packages.contains(pkgName)) { + if (packages != null && !packages.contains(pus.getPackageName())) { // This package has not been requested -- don't print // anything for it. } else if (isCompactOutput) { sb.append("P:"); - sb.append(pkgName); + sb.append(pus.getPackageName()); sb.append(','); - sb.append(pus.mLaunchCount); + sb.append(pus.getLaunchCount()); sb.append(','); - sb.append(pus.mUsageTime); + sb.append(pus.getUsageTime(time)); sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { - sb.append("A:"); + sb.append("L:"); String activity = pus.mLaunchTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mLaunchTimes.valueAt(i); @@ -1001,7 +1018,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } final int NFDT = pus.mFullyDrawnTimes.size(); for (int i=0; i<NFDT; i++) { - sb.append("A:"); + sb.append("D:"); String activity = pus.mFullyDrawnTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mFullyDrawnTimes.valueAt(i); @@ -1011,15 +1028,23 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + pw.print("R:"); pw.print(pus.componentResumeTimes.keyAt(c)); pw.print(","); + pw.println(pus.componentResumeTimes.valueAt(c)); + } } else { sb.append(" "); - sb.append(pkgName); - sb.append(": "); - sb.append(pus.mLaunchCount); - sb.append(" times, "); - sb.append(pus.mUsageTime); - sb.append(" ms"); + sb.append(pus.getPackageName()); + if (pus.getLaunchCount() != 0 || pus.getUsageTime(time) != 0) { + sb.append(": "); + sb.append(pus.getLaunchCount()); + sb.append(" times, "); + TimeUtils.formatDuration(pus.getUsageTime(time), sb); + } else { + sb.append(":"); + } sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { @@ -1084,10 +1109,50 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + sb.append(" "); + sb.append(pus.componentResumeTimes.keyAt(c)); + sb.append(" last resumed "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + pus.componentResumeTimes.valueAt(c)).toString()); + sb.append('\n'); + } } pw.write(sb.toString()); } + if (packages == null) { + int NC = stats.mConfigurations.size(); + for (int c=0; c<NC; c++) { + ConfigUsageStatsExtended cus + = (ConfigUsageStatsExtended)stats.mConfigurations.valueAt(c); + sb.setLength(0); + if (isCompactOutput) { + sb.append("C:"); sb.append(cus.getConfiguration().toString()); + sb.append(","); sb.append(cus.getUsageCount()); sb.append(","); + sb.append(cus.getUsageTime(time)); + } else { + sb.append(" "); + sb.append(cus.getConfiguration().toString()); + sb.append(":\n"); + if (cus.getUsageCount() != 0 || cus.getUsageTime(time) != 0) { + sb.append(" Used "); + sb.append(cus.getUsageCount()); + sb.append(" times, "); + TimeUtils.formatDuration(cus.getUsageTime(time), sb); + sb.append("\n"); + } + if (cus.getLastUsedTime() > 0) { + sb.append(" Last used: "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + cus.getLastUsedTime()).toString()); + sb.append("\n"); + } + } + pw.write(sb.toString()); + } + } } /** @@ -1174,5 +1239,4 @@ public final class UsageStatsService extends IUsageStats.Stub { collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages); } } - } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 3ae0fd5..3d5fb57 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -518,6 +518,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Brighten quickly. slow = false; } + // If low power mode is enabled, brightness level + // would be scaled down to half + if (mPowerRequest.lowPowerMode) { + target = target/2; + } animateScreenBrightness(clampScreenBrightness(target), slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); } else { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 54cb035..0f5805c 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -323,6 +323,10 @@ public class InputManagerService extends IInputManager.Stub mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES); mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS); + + if (mWiredAccessoryCallbacks != null) { + mWiredAccessoryCallbacks.systemReady(); + } } private void reloadKeyboardLayouts() { @@ -1588,6 +1592,7 @@ public class InputManagerService extends IInputManager.Stub */ public interface WiredAccessoryCallbacks { public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask); + public void systemReady(); } /** diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java new file mode 100644 index 0000000..c8b1ba0 --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -0,0 +1,44 @@ +/* + * 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 com.android.server.notification; + +import java.util.Comparator; + +/** + * Sorts notificaitons into attention-relelvant order. + */ +public class NotificationComparator + implements Comparator<NotificationManagerService.NotificationRecord> { + + @Override + public int compare(NotificationManagerService.NotificationRecord lhs, + NotificationManagerService.NotificationRecord rhs) { + final int leftScore = lhs.sbn.getScore(); + final int rightScore = rhs.sbn.getScore(); + if (leftScore != rightScore) { + // by priority, high to low + return -1 * Integer.compare(leftScore, rightScore); + } + final float leftPeple = lhs.getContactAffinity(); + final float rightPeople = rhs.getContactAffinity(); + if (leftPeple != rightPeople) { + // by contact proximity, close to far + return -1 * Float.compare(leftPeple, rightPeople); + } + // then break ties by time, most recent first + return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime()); + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index fce86e8..7a4f951 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -49,8 +49,10 @@ import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.IInterface; +import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -61,6 +63,7 @@ import android.service.notification.INotificationListener; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationOrderUpdate; import android.service.notification.StatusBarNotification; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; @@ -76,7 +79,6 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.internal.R; -import com.android.internal.notification.NotificationScorer; import com.android.internal.util.FastXmlSerializer; import com.android.server.EventLogTags; import com.android.server.SystemService; @@ -104,9 +106,12 @@ import java.lang.reflect.Array; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; /** {@hide} */ public class NotificationManagerService extends SystemService { @@ -118,6 +123,8 @@ public class NotificationManagerService extends SystemService { // message codes static final int MESSAGE_TIMEOUT = 2; static final int MESSAGE_SAVE_POLICY_FILE = 3; + static final int MESSAGE_RECONSIDER_RANKING = 4; + static final int MESSAGE_SEND_RANKING_UPDATE = 5; static final int LONG_DELAY = 3500; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds @@ -147,6 +154,9 @@ public class NotificationManagerService extends SystemService { final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; + private final HandlerThread mRankingThread = new HandlerThread("ranker", + Process.THREAD_PRIORITY_BACKGROUND); + private Handler mRankingHandler = null; private Light mNotificationLight; Light mAttentionLight; @@ -171,6 +181,7 @@ public class NotificationManagerService extends SystemService { // used as a mutex for access to all active notifications & listeners final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); + final NotificationComparator mRankingComparator = new NotificationComparator(); final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<String, NotificationRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); @@ -193,7 +204,7 @@ public class NotificationManagerService extends SystemService { private static final String TAG_PACKAGE = "package"; private static final String ATTR_NAME = "name"; - final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); + final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>(); private final UserProfiles mUserProfiles = new UserProfiles(); private NotificationListeners mListeners; @@ -444,8 +455,9 @@ public class NotificationManagerService extends SystemService { public static final class NotificationRecord { final StatusBarNotification sbn; - final SingleNotificationStats stats = new SingleNotificationStats(); + SingleNotificationStats stats; IBinder statusBarKey; + private float mContactAffinity; NotificationRecord(StatusBarNotification sbn) { @@ -528,6 +540,14 @@ public class NotificationManagerService extends SystemService { this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), this.sbn.getNotification()); } + + public void setContactAffinity(float contactAffinity) { + mContactAffinity = contactAffinity; + } + + public float getContactAffinity() { + return mContactAffinity; + } } private static final class ToastRecord @@ -707,7 +727,7 @@ public class NotificationManagerService extends SystemService { boolean queryRemove = false; boolean packageChanged = false; boolean cancelNotifications = true; - + if (action.equals(Intent.ACTION_PACKAGE_ADDED) || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) @@ -849,6 +869,8 @@ public class NotificationManagerService extends SystemService { mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); mHandler = new WorkerHandler(); + mRankingThread.start(); + mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper()); mZenModeHelper = new ZenModeHelper(getContext(), mHandler); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override @@ -925,21 +947,22 @@ public class NotificationManagerService extends SystemService { mSettingsObserver = new SettingsObserver(mHandler); - // spin up NotificationScorers - String[] notificationScorerNames = resources.getStringArray( - R.array.config_notificationScorers); - for (String scorerName : notificationScorerNames) { + // spin up NotificationSignalExtractors + String[] extractorNames = resources.getStringArray( + R.array.config_notificationSignalExtractors); + for (String extractorName : extractorNames) { try { - Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); - NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); - scorer.initialize(getContext()); - mScorers.add(scorer); + Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName); + NotificationSignalExtractor extractor = + (NotificationSignalExtractor) extractorClass.newInstance(); + extractor.initialize(getContext()); + mSignalExtractors.add(extractor); } catch (ClassNotFoundException e) { - Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); + Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e); } catch (InstantiationException e) { - Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); + Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e); } catch (IllegalAccessException e) { - Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); + Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e); } } @@ -1150,6 +1173,7 @@ public class NotificationManagerService extends SystemService { * System-only API for getting a list of current (i.e. not cleared) notifications. * * Requires ACCESS_NOTIFICATIONS which is signature|system. + * @returns A list of all the notifications, in natural order. */ @Override public StatusBarNotification[] getActiveNotifications(String callingPkg) { @@ -1306,6 +1330,9 @@ public class NotificationManagerService extends SystemService { * should be used. * * @param token The binder for the listener, to check that the caller is allowed + * @param keys the notification keys to fetch, or null for all active notifications. + * @returns The return value will contain the notifications specified in keys, in that + * order, or if keys is null, all the notifications, in natural order. */ @Override public StatusBarNotification[] getActiveNotificationsFromListener( @@ -1337,7 +1364,7 @@ public class NotificationManagerService extends SystemService { @Override public String[] getActiveNotificationKeysFromListener(INotificationListener token) { - return NotificationManagerService.this.getActiveNotificationKeysFromListener(token); + return NotificationManagerService.this.getActiveNotificationKeys(token); } @Override @@ -1409,19 +1436,21 @@ public class NotificationManagerService extends SystemService { } }; - private String[] getActiveNotificationKeysFromListener(INotificationListener token) { - synchronized (mNotificationList) { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - final ArrayList<String> keys = new ArrayList<String>(); - final int N = mNotificationList.size(); - for (int i=0; i<N; i++) { - final StatusBarNotification sbn = mNotificationList.get(i).sbn; - if (info.enabledAndUserMatches(sbn.getUserId())) { - keys.add(sbn.getKey()); + private String[] getActiveNotificationKeys(INotificationListener token) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + final ArrayList<String> keys = new ArrayList<String>(); + if (info.isEnabledForCurrentProfiles()) { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + for (int i = 0; i < N; i++) { + final StatusBarNotification sbn = mNotificationList.get(i).sbn; + if (info.enabledAndUserMatches(sbn.getUserId())) { + keys.add(sbn.getKey()); + } } } - return keys.toArray(new String[keys.size()]); } + return keys.toArray(new String[keys.size()]); } void dumpImpl(PrintWriter pw) { @@ -1578,26 +1607,23 @@ public class NotificationManagerService extends SystemService { // 1. initial score: buckets of 10, around the app int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] - // 2. Consult external heuristics (TBD) - - // 3. Apply local rules - - int initialScore = score; - if (!mScorers.isEmpty()) { - if (DBG) Slog.v(TAG, "Initial score is " + score + "."); - for (NotificationScorer scorer : mScorers) { + // 2. extract ranking signals from the notification data + final StatusBarNotification n = new StatusBarNotification( + pkg, opPkg, id, tag, callingUid, callingPid, score, notification, + user); + NotificationRecord r = new NotificationRecord(n); + if (!mSignalExtractors.isEmpty()) { + for (NotificationSignalExtractor extractor : mSignalExtractors) { try { - score = scorer.getScore(notification, score); + RankingFuture future = extractor.process(r); + scheduleRankingReconsideration(future); } catch (Throwable t) { - Slog.w(TAG, "Scorer threw on .getScore.", t); + Slog.w(TAG, "NotificationSignalExtractor failed.", t); } } - if (DBG) Slog.v(TAG, "Final score is " + score + "."); } - // add extra to indicate score modified by NotificationScorer - notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, - score != initialScore); + // 3. Apply local rules // blocked apps if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { @@ -1608,10 +1634,6 @@ public class NotificationManagerService extends SystemService { } } - if (DBG) { - Slog.v(TAG, "Assigned score=" + score + " to " + notification); - } - if (score < SCORE_DISPLAY_THRESHOLD) { // Notification will be blocked because the score is too low. return; @@ -1626,12 +1648,7 @@ public class NotificationManagerService extends SystemService { if (DBG || intercept) Slog.v(TAG, "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); synchronized (mNotificationList) { - final StatusBarNotification n = new StatusBarNotification( - pkg, opPkg, id, tag, callingUid, callingPid, score, notification, - user); - NotificationRecord r = new NotificationRecord(n); NotificationRecord old = null; - int index = indexOfNotificationLocked(pkg, tag, id, userId); if (index < 0) { mNotificationList.add(r); @@ -1639,7 +1656,7 @@ public class NotificationManagerService extends SystemService { } else { old = mNotificationList.get(index); mNotificationList.set(index, r); - mUsageStats.registerUpdatedByApp(r); + mUsageStats.registerUpdatedByApp(r, old); // Make sure we don't lose the foreground service state. if (old != null) { notification.flags |= @@ -1651,6 +1668,8 @@ public class NotificationManagerService extends SystemService { } mNotificationsByKey.put(n.getKey(), r); + Collections.sort(mNotificationList, mRankingComparator); + // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { @@ -1948,6 +1967,57 @@ public class NotificationManagerService extends SystemService { } } + private void scheduleRankingReconsideration(RankingFuture future) { + if (future != null) { + Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future); + long delay = future.getDelay(TimeUnit.MILLISECONDS); + mRankingHandler.sendMessageDelayed(m, delay); + } + } + + private void handleRankingReconsideration(Message message) { + if (!(message.obj instanceof RankingFuture)) return; + + RankingFuture future = (RankingFuture) message.obj; + future.run(); + try { + NotificationRecord record = future.get(); + synchronized (mNotificationList) { + int before = mNotificationList.indexOf(record); + if (before != -1) { + Collections.sort(mNotificationList, mRankingComparator); + int after = mNotificationList.indexOf(record); + + if (before != after) { + scheduleSendRankingUpdate(); + } + } + } + } catch (InterruptedException e) { + // we're running the future explicitly, so this should never happen + } catch (ExecutionException e) { + // we're running the future explicitly, so this should never happen + } + } + + private void scheduleSendRankingUpdate() { + mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); + Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); + mHandler.sendMessage(m); + } + + private void handleSendRankingUpdate() { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + ArrayList<StatusBarNotification> sbns = + new ArrayList<StatusBarNotification>(N); + for (int i = 0; i < N; i++ ) { + sbns.add(mNotificationList.get(i).sbn); + } + mListeners.notifyOrderUpdateLocked(sbns); + } + } + private final class WorkerHandler extends Handler { @Override @@ -1961,10 +2031,29 @@ public class NotificationManagerService extends SystemService { case MESSAGE_SAVE_POLICY_FILE: handleSavePolicyFile(); break; + case MESSAGE_SEND_RANKING_UPDATE: + handleSendRankingUpdate(); + break; } } + } + private final class RankingWorkerHandler extends Handler + { + public RankingWorkerHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_RECONSIDER_RANKING: + handleRankingReconsideration(msg); + break; + } + } + } // Notifications // ============================================================================ @@ -2346,9 +2435,9 @@ public class NotificationManagerService extends SystemService { @Override public void onServiceAdded(ManagedServiceInfo info) { final INotificationListener listener = (INotificationListener) info.service; - final String[] keys = getActiveNotificationKeysFromListener(listener); + final String[] keys = getActiveNotificationKeys(listener); try { - listener.onListenerConnected(keys); + listener.onListenerConnected(new NotificationOrderUpdate(keys)); } catch (RemoteException e) { // we tried } @@ -2361,12 +2450,18 @@ public class NotificationManagerService extends SystemService { // make a copy in case changes are made to the underlying Notification object final StatusBarNotification sbnClone = sbn.clone(); for (final ManagedServiceInfo info : mServices) { - mHandler.post(new Runnable() { - @Override - public void run() { - notifyPostedIfUserMatch(info, sbnClone); + if (info.isEnabledForCurrentProfiles()) { + final INotificationListener listener = (INotificationListener) info.service; + final String[] keys = getActiveNotificationKeys(listener); + if (keys.length > 0) { + mHandler.post(new Runnable() { + @Override + public void run() { + notifyPostedIfUserMatch(info, sbnClone, keys); + } + }); } - }); + } } } @@ -2378,39 +2473,83 @@ public class NotificationManagerService extends SystemService { // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification final StatusBarNotification sbnLight = sbn.cloneLight(); - for (ManagedServiceInfo serviceInfo : mServices) { - final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo; + for (final ManagedServiceInfo info : mServices) { + if (info.isEnabledForCurrentProfiles()) { + final INotificationListener listener = (INotificationListener) info.service; + final String[] keys = getActiveNotificationKeys(listener); + mHandler.post(new Runnable() { + @Override + public void run() { + notifyRemovedIfUserMatch(info, sbnLight, keys); + } + }); + } + } + } + + /** + * asynchronously notify all listeners about a reordering of notifications + * @param sbns an array of {@link StatusBarNotification}s to consider. This code + * must not rely on mutable members of these objects, such as the + * {@link Notification}. + */ + public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) { + for (final ManagedServiceInfo serviceInfo : mServices) { mHandler.post(new Runnable() { @Override public void run() { - notifyRemovedIfUserMatch(info, sbnLight); + notifyOrderUpdateIfUserMatch(serviceInfo, sbns); } }); } } - private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { + private void notifyPostedIfUserMatch(final ManagedServiceInfo info, + final StatusBarNotification sbn, String[] keys) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener)info.service; try { - listener.onNotificationPosted(sbn); + listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys)); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (posted): " + listener, ex); } } - private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { + private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, + String[] keys) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener)info.service; try { - listener.onNotificationRemoved(sbn); + listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys)); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (removed): " + listener, ex); } } + + /** + * @param sbns an array of {@link StatusBarNotification}s to consider. This code + * must not rely on mutable members of these objects, such as the + * {@link Notification}. + */ + public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info, + ArrayList<StatusBarNotification> sbns) { + ArrayList<String> keys = new ArrayList<String>(sbns.size()); + for (StatusBarNotification sbn: sbns) { + if (info.enabledAndUserMatches(sbn.getUserId())) { + keys.add(sbn.getKey()); + } + } + final INotificationListener listener = (INotificationListener)info.service; + try { + listener.onNotificationOrderUpdate( + new NotificationOrderUpdate(keys.toArray(new String[keys.size()]))); + } catch (RemoteException ex) { + Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); + } + } } } diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java new file mode 100644 index 0000000..a41fdfe --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java @@ -0,0 +1,41 @@ +/* +* 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 com.android.server.notification; + +import android.content.Context; + +/** + * Extracts signals that will be useful to the {@link NotificationComparator} and caches them + * on the {@link NotificationManagerService.NotificationRecord} object. These annotations will + * not be passed on to {@link android.service.notification.NotificationListenerService}s. + */ +public interface NotificationSignalExtractor { + + /** One-time initialization. */ + public void initialize(Context context); + + /** + * Called once per notification that is posted or updated. + * + * @return null if the work is done, or a future if there is more to do. The + * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered + * by that execution, the {@link NotificationManagerService} may send order update + * events to the {@link android.service.notification.NotificationListenerService}s. + */ + public RankingFuture process(NotificationManagerService.NotificationRecord notification); + +} diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 45ab3d3..a60e95b 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -58,6 +58,7 @@ public class NotificationUsageStats { * Called when a notification has been posted. */ public synchronized void registerPostedByApp(NotificationRecord notification) { + notification.stats = new SingleNotificationStats(); notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numPostedByApp++; @@ -68,7 +69,8 @@ public class NotificationUsageStats { /** * Called when a notification has been updated. */ - public void registerUpdatedByApp(NotificationRecord notification) { + public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) { + notification.stats = old.stats; for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numUpdatedByApp++; } diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java new file mode 100644 index 0000000..33aad8d --- /dev/null +++ b/services/core/java/com/android/server/notification/RankingFuture.java @@ -0,0 +1,118 @@ +/* + * 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 com.android.server.notification; + +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class RankingFuture + implements ScheduledFuture<NotificationManagerService.NotificationRecord> { + private static final long IMMEDIATE = 0l; + + private static final int START = 0; + private static final int RUNNING = 1; + private static final int DONE = 2; + private static final int CANCELLED = 3; + + private int mState; + private long mDelay; + protected NotificationManagerService.NotificationRecord mRecord; + + public RankingFuture(NotificationManagerService.NotificationRecord record) { + this(record, IMMEDIATE); + } + + public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) { + mDelay = delay; + mRecord = record; + mState = START; + } + + public void run() { + if (mState == START) { + mState = RUNNING; + + work(); + + mState = DONE; + synchronized (this) { + notifyAll(); + } + } + } + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(mDelay, TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(Delayed another) { + return Long.compare(getDelay(TimeUnit.MICROSECONDS), + another.getDelay(TimeUnit.MICROSECONDS)); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (mState == START) { // can't cancel if running or done + mState = CANCELLED; + return true; + } + return false; + } + + @Override + public boolean isCancelled() { + return mState == CANCELLED; + } + + @Override + public boolean isDone() { + return mState == DONE; + } + + @Override + public NotificationManagerService.NotificationRecord get() + throws InterruptedException, ExecutionException { + while (!isDone()) { + synchronized (this) { + this.wait(); + } + } + return mRecord; + } + + @Override + public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS); + long start = System.currentTimeMillis(); + long now = System.currentTimeMillis(); + while (!isDone() && (now - start) < timeoutMillis) { + try { + wait(timeoutMillis - (now - start)); + } catch (InterruptedException e) { + now = System.currentTimeMillis(); + } + } + return mRecord; + } + + public abstract void work(); +} diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java new file mode 100644 index 0000000..8cd2f9b2 --- /dev/null +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -0,0 +1,298 @@ +/* +* 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 com.android.server.notification; + +import android.app.Notification; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.LruCache; +import android.util.Slog; + +import com.android.server.notification.NotificationManagerService.NotificationRecord; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * This {@link NotificationSignalExtractor} attempts to validate + * people references. Also elevates the priority of real people. + */ +public class ValidateNotificationPeople implements NotificationSignalExtractor { + private static final String TAG = "ValidateNotificationPeople"; + private static final boolean INFO = true; + private static final boolean DEBUG = false; + + private static final boolean ENABLE_PEOPLE_VALIDATOR = true; + private static final String SETTING_ENABLE_PEOPLE_VALIDATOR = + "validate_notification_people_enabled"; + private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; + private static final int MAX_PEOPLE = 10; + private static final int PEOPLE_CACHE_SIZE = 200; + + private static final float NONE = 0f; + private static final float VALID_CONTACT = 0.5f; + // TODO private static final float STARRED_CONTACT = 1f; + + protected boolean mEnabled; + private Context mContext; + + // maps raw person handle to resolved person object + private LruCache<String, LookupResult> mPeopleCache; + + private RankingFuture validatePeople(NotificationRecord record) { + float affinity = NONE; + Bundle extras = record.getNotification().extras; + if (extras == null) { + return null; + } + + final String[] people = getExtraPeople(extras); + if (people == null || people.length == 0) { + return null; + } + + if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey()); + final LinkedList<String> pendingLookups = new LinkedList<String>(); + for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { + final String handle = people[personIdx]; + if (TextUtils.isEmpty(handle)) continue; + + synchronized (mPeopleCache) { + LookupResult lookupResult = mPeopleCache.get(handle); + if (lookupResult == null || lookupResult.isExpired()) { + pendingLookups.add(handle); + } else { + if (DEBUG) Slog.d(TAG, "using cached lookupResult: " + lookupResult.mId); + } + if (lookupResult != null) { + affinity = Math.max(affinity, lookupResult.getAffinity()); + } + } + } + + // record the best available data, so far: + record.setContactAffinity(affinity); + + if (pendingLookups.isEmpty()) { + if (INFO) Slog.i(TAG, "final affinity: " + affinity); + return null; + } + + if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey()); + return new RankingFuture(record) { + @Override + public void work() { + if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey()); + float affinity = NONE; + LookupResult lookupResult = null; + for (final String handle: pendingLookups) { + final Uri uri = Uri.parse(handle); + if ("tel".equals(uri.getScheme())) { + if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle); + lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart()); + } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { + if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); + lookupResult = resolveContactsUri(handle, uri); + } else { + Slog.w(TAG, "unsupported URI " + handle); + } + } + if (lookupResult != null) { + affinity = Math.max(affinity, lookupResult.getAffinity()); + } + + float affinityBound = mRecord.getContactAffinity(); + affinity = Math.max(affinity, affinityBound); + mRecord.setContactAffinity(affinity); + if (INFO) Slog.i(TAG, "final affinity: " + affinity); + } + }; + } + + private String[] getExtraPeople(Bundle extras) { + String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); + if (people != null) { + return people; + } + + ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE); + if (stringArray != null) { + return (String[]) stringArray.toArray(); + } + + String string = extras.getString(Notification.EXTRA_PEOPLE); + if (string != null) { + people = new String[1]; + people[0] = string; + return people; + } + char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE); + if (charArray != null) { + people = new String[1]; + people[0] = new String(charArray); + return people; + } + + CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE); + if (charSeq != null) { + people = new String[1]; + people[0] = charSeq.toString(); + return people; + } + + CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE); + if (charSeqArray != null) { + final int N = charSeqArray.length; + people = new String[N]; + for (int i = 0; i < N; i++) { + people[i] = charSeqArray[i].toString(); + } + return people; + } + + ArrayList<CharSequence> charSeqList = + extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE); + if (charSeqList != null) { + final int N = charSeqList.size(); + people = new String[N]; + for (int i = 0; i < N; i++) { + people[i] = charSeqList.get(i).toString(); + } + return people; + } + return null; + } + + private LookupResult resolvePhoneContact(final String handle, final String number) { + LookupResult lookupResult = null; + Cursor c = null; + try { + Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(number)); + c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); + if (c != null && c.getCount() > 0) { + c.moveToFirst(); + final int idIdx = c.getColumnIndex(Contacts._ID); + final int id = c.getInt(idIdx); + if (DEBUG) Slog.d(TAG, "is valid: " + id); + lookupResult = new LookupResult(id); + } + } catch(Throwable t) { + Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); + } finally { + if (c != null) { + c.close(); + } + } + if (lookupResult == null) { + lookupResult = new LookupResult(LookupResult.INVALID_ID); + } + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + return lookupResult; + } + + private LookupResult resolveContactsUri(String handle, final Uri personUri) { + LookupResult lookupResult = null; + Cursor c = null; + try { + c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); + if (c != null && c.getCount() > 0) { + c.moveToFirst(); + final int idIdx = c.getColumnIndex(Contacts._ID); + final int id = c.getInt(idIdx); + if (DEBUG) Slog.d(TAG, "is valid: " + id); + lookupResult = new LookupResult(id); + } + } catch(Throwable t) { + Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); + } finally { + if (c != null) { + c.close(); + } + } + if (lookupResult == null) { + lookupResult = new LookupResult(LookupResult.INVALID_ID); + } + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + return lookupResult; + } + + public void initialize(Context context) { + if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + mContext = context; + mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); + mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( + mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); + } + + public RankingFuture process(NotificationManagerService.NotificationRecord record) { + if (!mEnabled) { + if (INFO) Slog.i(TAG, "disabled"); + return null; + } + if (record == null || record.getNotification() == null) { + if (INFO) Slog.i(TAG, "skipping empty notification"); + return null; + } + return validatePeople(record); + } + + private static class LookupResult { + private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr + public static final int INVALID_ID = -1; + + private final long mExpireMillis; + private int mId; + + public LookupResult(int id) { + mId = id; + mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; + } + + public boolean isExpired() { + return mExpireMillis < System.currentTimeMillis(); + } + + public boolean isInvalid() { + return mId == INVALID_ID || isExpired(); + } + + public float getAffinity() { + if (isInvalid()) { + return NONE; + } else { + return VALID_CONTACT; // TODO: finer grained result: stars + } + } + + public LookupResult setId(int id) { + mId = id; + return this; + } + } +} + diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c8b61f1..a7f4b28 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -613,6 +613,12 @@ public class PackageManagerService extends IPackageManager.Stub { private final AtomicLong mLastWritten = new AtomicLong(0); private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false); + private boolean mIsFirstBoot = false; + + boolean isFirstBoot() { + return mIsFirstBoot; + } + void write(boolean force) { if (force) { write(); @@ -701,6 +707,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.mLastPackageUsageTimeInMills = timeInMillis; } } catch (FileNotFoundException expected) { + mIsFirstBoot = true; } catch (IOException e) { Log.w(TAG, "Failed to read package usage times", e); } finally { @@ -1745,7 +1752,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public boolean isFirstBoot() { - return !mRestoredSettings; + return !mRestoredSettings || mPackageUsage.isFirstBoot(); } @Override diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 60c6313..60212bf 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -288,6 +288,20 @@ public class UserManagerService extends IUserManager.Stub { return users; } + @Override + public UserInfo getProfileParent(int userHandle) { + checkManageUsersPermission("get the profile parent"); + synchronized (mPackagesLock) { + UserInfo profile = getUserInfoLocked(userHandle); + int parentUserId = profile.profileGroupId; + if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) { + return null; + } else { + return getUserInfoLocked(parentUserId); + } + } + } + private boolean isProfileOf(UserInfo user, UserInfo profile) { return user.id == profile.id || (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID @@ -1022,17 +1036,6 @@ public class UserManagerService extends IUserManager.Stub { } } - private int getNextProfileGroupIdLocked() { - int maxGroupId = UserInfo.NO_PROFILE_GROUP_ID; - for (int i = 0; i < mUsers.size(); i++) { - UserInfo ui = mUsers.valueAt(i); - if (maxGroupId < ui.profileGroupId) { - maxGroupId = ui.profileGroupId; - } - } - return maxGroupId + 1; - } - @Override public UserInfo createProfileForUser(String name, int flags, int userId) { checkManageUsersPermission("Only the system can create users"); @@ -1049,16 +1052,16 @@ public class UserManagerService extends IUserManager.Stub { return createUserInternal(name, flags, UserHandle.USER_NULL); } - private UserInfo createUserInternal(String name, int flags, int profileId) { + private UserInfo createUserInternal(String name, int flags, int parentId) { final long ident = Binder.clearCallingIdentity(); UserInfo userInfo = null; try { synchronized (mInstallLock) { synchronized (mPackagesLock) { - UserInfo profile = null; - if (profileId != UserHandle.USER_NULL) { - profile = getUserInfoLocked(profileId); - if (profile == null) return null; + UserInfo parent = null; + if (parentId != UserHandle.USER_NULL) { + parent = getUserInfoLocked(parentId); + if (parent == null) return null; } if (isUserLimitReachedLocked()) return null; int userId = getNextAvailableIdLocked(); @@ -1071,12 +1074,12 @@ public class UserManagerService extends IUserManager.Stub { Environment.getUserSystemDirectory(userInfo.id).mkdirs(); mUsers.put(userId, userInfo); writeUserListLocked(); - if (profile != null) { - if (profile.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { - profile.profileGroupId = getNextProfileGroupIdLocked(); - writeUserLocked(profile); + if (parent != null) { + if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { + parent.profileGroupId = parent.id; + writeUserLocked(parent); } - userInfo.profileGroupId = profile.profileGroupId; + userInfo.profileGroupId = parent.profileGroupId; } writeUserLocked(userInfo); mPm.createNewUserLILPw(userId, userPath); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 6d2e859..47a8b2e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1634,6 +1634,8 @@ public final class PowerManagerService extends com.android.server.SystemService mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld(); + mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled; + mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 9039236..99ec242 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -116,7 +116,8 @@ public class AppTransition implements Dump { /** Fraction of animation at which the recents thumbnail becomes completely transparent */ private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f; - private static final long DEFAULT_APP_TRANSITION_DURATION = 250; + private static final int DEFAULT_APP_TRANSITION_DURATION = 250; + private static final int THUMBNAIL_APP_TRANSITION_DURATION = 225; private final Context mContext; private final Handler mH; @@ -160,6 +161,7 @@ public class AppTransition implements Dump { private final int mConfigShortAnimTime; private final Interpolator mDecelerateInterpolator; private final Interpolator mThumbnailFadeoutInterpolator; + private final Interpolator mThumbnailCubicInterpolator; private int mCurrentUserId = 0; @@ -170,6 +172,8 @@ public class AppTransition implements Dump { com.android.internal.R.integer.config_shortAnimTime); mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.decelerate_cubic); + mThumbnailCubicInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); mThumbnailFadeoutInterpolator = new Interpolator() { @Override public float getInterpolation(float input) { @@ -401,11 +405,23 @@ public class AppTransition implements Dump { /** * Prepares the specified animation with a standard duration, interpolator, etc. */ + Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight, + int duration, Interpolator interpolator) { + a.setDuration(duration); + a.setFillAfter(true); + a.setInterpolator(interpolator); + a.initialize(appWidth, appHeight, appWidth, appHeight); + return a; + } + + /** + * Prepares the specified animation with a standard duration, interpolator, etc. + */ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. - final long duration; + final int duration; switch (transit) { case TRANSIT_ACTIVITY_OPEN: case TRANSIT_ACTIVITY_CLOSE: @@ -415,11 +431,8 @@ public class AppTransition implements Dump { duration = DEFAULT_APP_TRANSITION_DURATION; break; } - a.setDuration(duration); - a.setFillAfter(true); - a.setInterpolator(mDecelerateInterpolator); - a.initialize(appWidth, appHeight, appWidth, appHeight); - return a; + return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration, + mDecelerateInterpolator); } /** @@ -594,7 +607,8 @@ public class AppTransition implements Dump { throw new RuntimeException("Invalid thumbnail transition state"); } - return prepareThumbnailAnimation(a, appWidth, appHeight, transit); + return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, + THUMBNAIL_APP_TRANSITION_DURATION, mThumbnailCubicInterpolator); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 637beec..836a19c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5038,6 +5038,10 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } + if (token == null) { + throw new IllegalArgumentException("token == null"); + } + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag))); } @@ -5049,6 +5053,10 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } + if (token == null) { + throw new IllegalArgumentException("token == null"); + } + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( KeyguardDisableHandler.KEYGUARD_REENABLE, token)); } @@ -5062,6 +5070,11 @@ public class WindowManagerService extends IWindowManager.Stub != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } + + if (callback == null) { + throw new IllegalArgumentException("callback == null"); + } + mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() { @Override public void onKeyguardExitResult(boolean success) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9a9f1c8..1980d1e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -97,6 +97,7 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -113,6 +114,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String DEVICE_POLICIES_XML = "device_policies.xml"; + private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = 86400 * 1000; @@ -127,6 +130,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final boolean DBG = false; final Context mContext; + final UserManager mUserManager; final PowerManager.WakeLock mWakeLock; IPowerManager mIPowerManager; @@ -182,6 +186,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ArrayList<ActiveAdmin> mAdminList = new ArrayList<ActiveAdmin>(); + // This is the list of component allowed to start lock task mode. + final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>(); + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -203,7 +210,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + action + " for user " + userHandle); mHandler.post(new Runnable() { public void run() { - handlePasswordExpirationNotification(getUserData(userHandle)); + handlePasswordExpirationNotification(userHandle); } }); } @@ -605,6 +612,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ public DevicePolicyManagerService(Context context) { mContext = context; + mUserManager = UserManager.get(mContext); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_DEVICE_ADMIN); mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)) @@ -812,6 +820,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { sendAdminCommandLocked(admin, action, null); } + /** + * Send an update to one specific admin, get notified when that admin returns a result. + */ void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) { Intent intent = new Intent(action); intent.setComponent(admin.info.getComponent()); @@ -826,12 +837,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * Send an update to all admins of a user that enforce a specified policy. + */ void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) { final DevicePolicyData policy = getUserData(userHandle); final int count = policy.mAdminList.size(); if (count > 0) { for (int i = 0; i < count; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); + final ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(reqPolicy)) { sendAdminCommandLocked(admin, action); } @@ -839,6 +853,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * Send an update intent to all admins of a user and its profiles. Only send to admins that + * enforce a specified policy. + */ + private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy, + int userHandle) { + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo ui : profiles) { + int id = ui.getUserHandle().getIdentifier(); + sendAdminCommandLocked(action, reqPolicy, id); + } + } + void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin != null) { @@ -955,6 +982,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "active-password"); } + for (int i=0; i<policy.mLockTaskComponents.size(); i++) { + ComponentName component = policy.mLockTaskComponents.get(i); + out.startTag(null, LOCK_TASK_COMPONENTS_XML); + out.attribute(null, "name", component.flattenToString()); + out.endTag(null, LOCK_TASK_COMPONENTS_XML); + } + out.endTag(null, "policies"); out.endDocument(); @@ -1004,6 +1038,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } type = parser.next(); int outerDepth = parser.getDepth(); + policy.mLockTaskComponents.clear(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -1056,6 +1091,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); XmlUtils.skipCurrentTag(parser); + } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) { + policy.mLockTaskComponents.add + (ComponentName.unflattenFromString + (parser.getAttributeValue(null, "name"))); + XmlUtils.skipCurrentTag(parser); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1171,23 +1211,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void handlePasswordExpirationNotification(DevicePolicyData policy) { + private void handlePasswordExpirationNotification(int userHandle) { synchronized (this) { final long now = System.currentTimeMillis(); - final int N = policy.mAdminList.size(); - if (N <= 0) { - return; - } - for (int i=0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) - && admin.passwordExpirationTimeout > 0L - && admin.passwordExpirationDate > 0L - && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) { - sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING); + + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo ui : profiles) { + int profileUserHandle = ui.getUserHandle().getIdentifier(); + final DevicePolicyData policy = getUserData(profileUserHandle); + final int count = policy.mAdminList.size(); + if (count > 0) { + for (int i = 0; i < count; i++) { + final ActiveAdmin admin = policy.mAdminList.get(i); + if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) + && admin.passwordExpirationTimeout > 0L + && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS + && admin.passwordExpirationDate > 0L) { + sendAdminCommandLocked(admin, + DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING); + } + } } } - setExpirationAlarmCheckLocked(mContext, policy); + setExpirationAlarmCheckLocked(mContext, getUserData(userHandle)); } } @@ -1197,8 +1243,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); if (! hasCert) { if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - for (UserInfo user : um.getUsers()) { + for (UserInfo user : mUserManager.getUsers()) { notificationManager.cancelAsUser( null, MONITORING_CERT_NOTIFICATION_ID, user.getUserHandle()); } @@ -1237,8 +1282,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // If this is a boot intent, this will fire for each user. But if this is a storage changed // intent, it will fire once, so we need to notify all users. if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - for (UserInfo user : um.getUsers()) { + for (UserInfo user : mUserManager.getUsers()) { notificationManager.notifyAsUser( null, MONITORING_CERT_NOTIFICATION_ID, noti, user.getUserHandle()); } @@ -1415,18 +1459,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceCrossUserPermission(userHandle); synchronized (this) { int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - DevicePolicyData policy = getUserData(userHandle); if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordQuality : mode; } - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (mode < admin.passwordQuality) { - mode = admin.passwordQuality; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (mode < admin.passwordQuality) { + mode = admin.passwordQuality; + } } } return mode; @@ -1457,7 +1505,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceCrossUserPermission(userHandle); synchronized (this) { - DevicePolicyData policy = getUserData(userHandle); int length = 0; if (who != null) { @@ -1465,11 +1512,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordLength : length; } - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordLength) { - length = admin.minimumPasswordLength; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordLength) { + length = admin.minimumPasswordLength; + } } } return length; @@ -1500,7 +1552,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceCrossUserPermission(userHandle); synchronized (this) { - DevicePolicyData policy = getUserData(userHandle); int length = 0; if (who != null) { @@ -1508,11 +1559,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.passwordHistoryLength : length; } - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.passwordHistoryLength) { - length = admin.passwordHistoryLength; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.passwordHistoryLength) { + length = admin.passwordHistoryLength; + } } } return length; @@ -1558,19 +1614,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceCrossUserPermission(userHandle); synchronized (this) { + long timeout = 0L; + if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); - return admin != null ? admin.passwordExpirationTimeout : 0L; + return admin != null ? admin.passwordExpirationTimeout : timeout; } - long timeout = 0L; - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (timeout == 0L || (admin.passwordExpirationTimeout != 0L - && timeout > admin.passwordExpirationTimeout)) { - timeout = admin.passwordExpirationTimeout; + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (timeout == 0L || (admin.passwordExpirationTimeout != 0L + && timeout > admin.passwordExpirationTimeout)) { + timeout = admin.passwordExpirationTimeout; + } } } return timeout; @@ -1582,19 +1642,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * Returns 0 if not configured. */ private long getPasswordExpirationLocked(ComponentName who, int userHandle) { + long timeout = 0L; + if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); - return admin != null ? admin.passwordExpirationDate : 0L; + return admin != null ? admin.passwordExpirationDate : timeout; } - long timeout = 0L; - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (timeout == 0L || (admin.passwordExpirationDate != 0 - && timeout > admin.passwordExpirationDate)) { - timeout = admin.passwordExpirationDate; + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (timeout == 0L || (admin.passwordExpirationDate != 0 + && timeout > admin.passwordExpirationDate)) { + timeout = admin.passwordExpirationDate; + } } } return timeout; @@ -1641,12 +1705,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordUpperCase : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordUpperCase) { - length = admin.minimumPasswordUpperCase; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordUpperCase) { + length = admin.minimumPasswordUpperCase; + } } } return length; @@ -1681,12 +1749,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordLowerCase : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordLowerCase) { - length = admin.minimumPasswordLowerCase; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordLowerCase) { + length = admin.minimumPasswordLowerCase; + } } } return length; @@ -1724,12 +1796,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordLetters : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordLetters) { - length = admin.minimumPasswordLetters; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordLetters) { + length = admin.minimumPasswordLetters; + } } } return length; @@ -1767,12 +1843,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordNumeric : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordNumeric) { - length = admin.minimumPasswordNumeric; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordNumeric) { + length = admin.minimumPasswordNumeric; + } } } return length; @@ -1810,12 +1890,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordSymbols : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordSymbols) { - length = admin.minimumPasswordSymbols; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordSymbols) { + length = admin.minimumPasswordSymbols; + } } } return length; @@ -1853,12 +1937,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.minimumPasswordNonLetter : length; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (length < admin.minimumPasswordNonLetter) { - length = admin.minimumPasswordNonLetter; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (length < admin.minimumPasswordNonLetter) { + length = admin.minimumPasswordNonLetter; + } } } return length; @@ -1870,8 +1958,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return true; } enforceCrossUserPermission(userHandle); + synchronized (this) { - DevicePolicyData policy = getUserData(userHandle); + + // The active password is stored in the user that runs the launcher + // If the user this is called from is part of a profile group, that is the parent + // of the group. + UserInfo parent = getProfileParent(userHandle); + int id = parent == null ? userHandle : parent.id; + DevicePolicyData policy = getUserData(id); + // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, @@ -1893,13 +1989,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public int getCurrentFailedPasswordAttempts(int userHandle) { - enforceCrossUserPermission(userHandle); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); - return getUserData(userHandle).mFailedPasswordAttempts; + + // The active password is stored in the parent. + DevicePolicyData policy = getUserData(getProfileParent(userHandle).id); + + return policy.mFailedPasswordAttempts; } } @@ -1909,6 +2008,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceCrossUserPermission(userHandle); synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(who, @@ -1928,7 +2030,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceCrossUserPermission(userHandle); synchronized (this) { - DevicePolicyData policy = getUserData(userHandle); int count = 0; if (who != null) { @@ -1936,14 +2037,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.maximumFailedPasswordsForWipe : count; } - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (count == 0) { - count = admin.maximumFailedPasswordsForWipe; - } else if (admin.maximumFailedPasswordsForWipe != 0 - && count > admin.maximumFailedPasswordsForWipe) { - count = admin.maximumFailedPasswordsForWipe; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (count == 0) { + count = admin.maximumFailedPasswordsForWipe; + } else if (admin.maximumFailedPasswordsForWipe != 0 + && count > admin.maximumFailedPasswordsForWipe) { + count = admin.maximumFailedPasswordsForWipe; + } } } return count; @@ -1955,9 +2061,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } enforceCrossUserPermission(userHandle); + enforceNotManagedProfile(userHandle, "reset the password"); + int quality; synchronized (this) { - // This API can only be called by an active device admin, + // This api can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); @@ -2135,15 +2243,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.maximumTimeToUnlock : time; } - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (time == 0) { - time = admin.maximumTimeToUnlock; - } else if (admin.maximumTimeToUnlock != 0 - && time > admin.maximumTimeToUnlock) { - time = admin.maximumTimeToUnlock; + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (time == 0) { + time = admin.maximumTimeToUnlock; + } else if (admin.maximumTimeToUnlock != 0 + && time > admin.maximumTimeToUnlock) { + time = admin.maximumTimeToUnlock; + } } } return time; @@ -2301,7 +2413,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void run() { try { ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER); - ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) + (mUserManager) .removeUser(userHandle); } catch (RemoteException re) { // Shouldn't happen @@ -2349,6 +2461,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } enforceCrossUserPermission(userHandle); + enforceNotManagedProfile(userHandle, "set the active password"); + mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); DevicePolicyData p = getUserData(userHandle); @@ -2377,7 +2491,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { saveSettingsLocked(userHandle); updatePasswordExpirationsLocked(userHandle); setExpirationAlarmCheckLocked(mContext, p); - sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, + sendAdminCommandToSelfAndProfilesLocked( + DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); } finally { Binder.restoreCallingIdentity(ident); @@ -2387,26 +2502,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Called any time the device password is updated. Resets all password expiration clocks. + * Called any time the device password is updated. Resets all password expiration clocks. */ private void updatePasswordExpirationsLocked(int userHandle) { - DevicePolicyData policy = getUserData(userHandle); - final int N = policy.mAdminList.size(); - if (N > 0) { - for (int i=0; i<N; i++) { - ActiveAdmin admin = policy.mAdminList.get(i); - if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) { - long timeout = admin.passwordExpirationTimeout; - long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; - admin.passwordExpirationDate = expiration; + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + for (UserInfo userInfo : profiles) { + int profileId = userInfo.getUserHandle().getIdentifier(); + DevicePolicyData policy = getUserData(profileId); + final int N = policy.mAdminList.size(); + if (N > 0) { + for (int i=0; i<N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) { + long timeout = admin.passwordExpirationTimeout; + long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; + admin.passwordExpirationDate = expiration; + } + } } + saveSettingsLocked(profileId); } - saveSettingsLocked(userHandle); - } } public void reportFailedPasswordAttempt(int userHandle) { enforceCrossUserPermission(userHandle); + enforceNotManagedProfile(userHandle, "report failed password attempt"); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); @@ -2421,7 +2541,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (max > 0 && policy.mFailedPasswordAttempts >= max) { wipeDeviceOrUserLocked(0, userHandle); } - sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED, + sendAdminCommandToSelfAndProfilesLocked( + DeviceAdminReceiver.ACTION_PASSWORD_FAILED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } finally { @@ -2444,7 +2565,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mPasswordOwner = -1; saveSettingsLocked(userHandle); if (mHasFeature) { - sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, + sendAdminCommandToSelfAndProfilesLocked( + DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } finally { @@ -2473,7 +2595,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Scan through active admins and find if anyone has already // set the global proxy. Set<ComponentName> compSet = policy.mAdminMap.keySet(); - for (ComponentName component : compSet) { + for (ComponentName component : compSet) { ActiveAdmin ap = policy.mAdminMap.get(component); if ((ap.specifiesGlobalProxy) && (!component.equals(who))) { // Another admin already sets the global proxy @@ -2502,8 +2624,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Reset the global proxy accordingly // Do this using system permissions, as apps cannot write to secure settings long origId = Binder.clearCallingIdentity(); - resetGlobalProxyLocked(policy); - Binder.restoreCallingIdentity(origId); + try { + resetGlobalProxyLocked(policy); + } finally { + Binder.restoreCallingIdentity(origId); + } return null; } } @@ -2888,8 +3013,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (um.getUserInfo(userHandle) == null) { + if (mUserManager.getUserInfo(userHandle) == null) { // User doesn't exist. throw new IllegalArgumentException( "Attempted to set profile owner for invalid userId: " + userHandle); @@ -2935,10 +3059,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = UserHandle.getCallingUserId(); Slog.d(LOG_TAG, "Enabling the profile for: " + userId); - UserManager um = UserManager.get(mContext); long id = Binder.clearCallingIdentity(); try { - um.setUserEnabled(userId); + mUserManager.setUserEnabled(userId); Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED); intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId())); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | @@ -3002,6 +3125,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private void enforceNotManagedProfile(int userHandle, String message) { + if(isManagedProfile(userHandle)) { + throw new SecurityException("You can not " + message + " from a managed profile. "); + } + } + + private UserInfo getProfileParent(int userHandle) { + long ident = Binder.clearCallingIdentity(); + try { + return mUserManager.getProfileParent(userHandle); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private boolean isManagedProfile(int userHandle) { + long ident = Binder.clearCallingIdentity(); + try { + return mUserManager.getUserInfo(userHandle).isManagedProfile(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + private void enableIfNecessary(String packageName, int userId) { try { IPackageManager ipm = AppGlobals.getPackageManager(); @@ -3105,10 +3252,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - UserManager um = UserManager.get(mContext); long id = Binder.clearCallingIdentity(); try { - um.setApplicationRestrictions(packageName, settings, userHandle); + mUserManager.setApplicationRestrictions(packageName, settings, userHandle); } finally { restoreCallingIdentity(id); } @@ -3172,10 +3318,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - UserManager um = UserManager.get(mContext); long id = Binder.clearCallingIdentity(); try { - return um.getApplicationRestrictions(packageName, userHandle); + return mUserManager.getApplicationRestrictions(packageName, userHandle); } finally { restoreCallingIdentity(id); } @@ -3192,10 +3337,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - UserManager um = UserManager.get(mContext); long id = Binder.clearCallingIdentity(); try { - um.setUserRestriction(key, enabled, userHandle); + mUserManager.setUserRestriction(key, enabled, userHandle); } finally { restoreCallingIdentity(id); } @@ -3344,4 +3488,74 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return resultSet.toArray(new String[resultSet.size()]); } } + + /** + * Sets which componets may enter lock task mode. + * + * This function can only be called by the device owner or the profile owner. + * @param components The list of components allowed to enter lock task mode. + */ + public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + // Get the package names of the caller. + int uid = Binder.getCallingUid(); + String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); + + // Check whether any of the package name is the device owner or the profile owner. + for (int i=0; i<packageNames.length; i++) { + String packageName = packageNames[i]; + int userHandle = UserHandle.getUserId(uid); + String profileOwnerPackage = getProfileOwner(userHandle); + if (isDeviceOwner(packageName) || + (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) { + + // If a package name is the device owner or the profile owner, + // we update the component list. + DevicePolicyData policy = getUserData(userHandle); + policy.mLockTaskComponents.clear(); + if (components != null) { + for (int j=0; j<components.length; j++) { + ComponentName component = components[j]; + policy.mLockTaskComponents.add(component); + } + } + + // Store the settings persistently. + saveSettingsLocked(userHandle); + return; + } + } + throw new SecurityException(); + } + + /** + * This function returns the list of components allowed to start the task lock mode. + */ + public ComponentName[] getLockTaskComponents() { + int userHandle = UserHandle.USER_OWNER; + DevicePolicyData policy = getUserData(userHandle); + ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]); + return tempArray; + } + + /** + * This function lets the caller know whether the given component is allowed to start the + * lock task mode. + * @param component The component to check + */ + public boolean isLockTaskPermitted(ComponentName component) { + // Get current user's devicepolicy + int uid = Binder.getCallingUid(); + int userHandle = UserHandle.getUserId(uid); + DevicePolicyData policy = getUserData(userHandle); + for (int i=0; i<policy.mLockTaskComponents.size(); i++) { + ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i); + + // If the given component equals one of the component stored our device-owner-set + // list, we allow this component to start the lock task mode. + if (lockTaskComponent.getPackageName().equals(component.getPackageName())) { + return true; + } + } + return false; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 716823c..22e2a6e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -334,6 +334,7 @@ public final class SystemServer { InputManagerService inputManager = null; TelephonyRegistry telephonyRegistry = null; ConsumerIrService consumerIr = null; + AudioService audioService = null; boolean onlyCore = false; boolean firstBoot = false; @@ -769,7 +770,8 @@ public final class SystemServer { if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) { try { Slog.i(TAG, "Audio Service"); - ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context)); + audioService = new AudioService(context); + ServiceManager.addService(Context.AUDIO_SERVICE, audioService); } catch (Throwable e) { reportWtf("starting Audio Service", e); } @@ -1084,6 +1086,7 @@ public final class SystemServer { final InputManagerService inputManagerF = inputManager; final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; + final AudioService audioServiceF = audioService; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -1152,6 +1155,11 @@ public final class SystemServer { } catch (Throwable e) { reportWtf("making Recognition Service ready", e); } + try { + if (audioServiceF != null) audioServiceF.systemReady(); + } catch (Throwable e) { + reportWtf("Notifying AudioService running", e); + } Watchdog.getInstance().start(); // It is now okay to let the various system services start their diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk new file mode 100644 index 0000000..7884d4d --- /dev/null +++ b/tests/Split/Android.mk @@ -0,0 +1,28 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := Split + +LOCAL_AAPT_FLAGS := --split fr,de +LOCAL_AAPT_FLAGS += -v + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml new file mode 100644 index 0000000..a4956a7 --- /dev/null +++ b/tests/Split/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.split"> + <application android:label="@string/app_title"> + <activity android:name="ActivityMain"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/Split/assets/blah.txt b/tests/Split/assets/blah.txt new file mode 100644 index 0000000..1b37e40 --- /dev/null +++ b/tests/Split/assets/blah.txt @@ -0,0 +1 @@ +This is some useful info. diff --git a/packages/SystemUI/res/drawable/ic_notify_clear.xml b/tests/Split/assets/statement.xml index 2163198..91750d1 100644 --- a/packages/SystemUI/res/drawable/ic_notify_clear.xml +++ b/tests/Split/assets/statement.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- 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. @@ -14,8 +14,6 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ic_notify_clear_normal" /> - <item android:drawable="@drawable/ic_notify_clear_normal" /> -</selector> +<statement> + <value>Hello</value> +</statement> diff --git a/tests/Split/res/layout-fr-sw600dp/main.xml b/tests/Split/res/layout-fr-sw600dp/main.xml new file mode 100644 index 0000000..2461c8c --- /dev/null +++ b/tests/Split/res/layout-fr-sw600dp/main.xml @@ -0,0 +1,23 @@ +<?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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</FrameLayout> diff --git a/packages/SystemUI/res/drawable/ic_notifications.xml b/tests/Split/res/layout/main.xml index 97a7623..36992a2 100644 --- a/packages/SystemUI/res/drawable/ic_notifications.xml +++ b/tests/Split/res/layout/main.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<!-- 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. @@ -14,10 +14,6 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ic_notify_open_normal" /> - <item - android:drawable="@drawable/ic_notify_open_normal" /> -</selector> - +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/packages/SystemUI/res/drawable/ic_notify_settings.xml b/tests/Split/res/values-de/values.xml index 9303ca4..26d0507 100644 --- a/packages/SystemUI/res/drawable/ic_notify_settings.xml +++ b/tests/Split/res/values-de/values.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<!-- 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. @@ -14,10 +14,6 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ic_notify_settings_normal" /> - <item - android:drawable="@drawable/ic_notify_settings_normal" /> -</selector> - +<resources> + <string name="test">Achtung!</string> +</resources> diff --git a/tests/Split/res/values-fr/values.xml b/tests/Split/res/values-fr/values.xml new file mode 100644 index 0000000..16532da --- /dev/null +++ b/tests/Split/res/values-fr/values.xml @@ -0,0 +1,25 @@ +<?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> + <string name="app_title">APK Divisé</string> + <string name="test">Bonjour, Monde!</string> + <string name="blah">Bleh..</string> + <string-array name="lotsofstrings"> + <item>Hé là </item> + <item>Au revoir</item> + </string-array> +</resources> diff --git a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml b/tests/Split/res/values-sw600dp/values.xml index 7cf3175..a8329bb 100644 --- a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml +++ b/tests/Split/res/values-sw600dp/values.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<!-- 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. @@ -14,10 +14,6 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ic_notify_quicksettings_normal" /> - <item - android:drawable="@drawable/ic_notify_quicksettings_normal" /> -</selector> - +<resources> + <dimen name="width">230dp</dimen> +</resources> diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml new file mode 100644 index 0000000..68edc77 --- /dev/null +++ b/tests/Split/res/values/values.xml @@ -0,0 +1,47 @@ +<?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> + <string name="app_title">Split APK</string> + <string name="test">Hello, World!</string> + <string name="boom">Boom!</string> + <string name="blah">Blah...</string> + <string-array name="lotsofstrings"> + <item>Hello there</item> + <item>Good bye</item> + </string-array> + + <plurals name="plur"> + <item quantity="zero">I no haz :(</item> + <item quantity="one">I haz 1!1! :)</item> + <item quantity="many">I haz ALL!</item> + </plurals> + + <bool name="que">true</bool> + <color name="green">#00FF00</color> + <dimen name="width">23dp</dimen> + <item type="id" name="identifier" /> + <integer name="number">123</integer> + <integer-array name="numList"> + <item>1234</item> + </integer-array> + + <array name="ary"> + <item>@string/test</item> + <item>@string/boom</item> + <item>25dp</item> + </array> +</resources> diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java new file mode 100644 index 0000000..a15fb3c --- /dev/null +++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java @@ -0,0 +1,31 @@ +/* + * 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 com.android.example.split; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class ActivityMain extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView text = new TextView(this); + text.setText(R.string.test); + setContentView(text); + } +} diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index e0dab78..12d5389 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -3,8 +3,10 @@ // #include "AaptAssets.h" -#include "ResourceFilter.h" +#include "AaptConfig.h" +#include "AaptUtil.h" #include "Main.h" +#include "ResourceFilter.h" #include <utils/misc.h> #include <utils/SortedVector.h> @@ -14,7 +16,6 @@ #include <errno.h> static const char* kDefaultLocale = "default"; -static const char* kWildcardName = "any"; static const char* kAssetDir = "assets"; static const char* kResourceDir = "res"; static const char* kValuesDir = "values"; @@ -149,24 +150,6 @@ static bool isHidden(const char *root, const char *path) // ========================================================================= // ========================================================================= -/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars, - Vector<String8>* parts, const char separator) { - const char *p = chars; - const char *q; - while (NULL != (q = strchr(p, separator))) { - String8 val(p, q - p); - val.toLower(); - parts->add(val); - p = q+1; - } - - if (p < chars + strlen(chars)) { - String8 val(p); - val.toLower(); - parts->add(val); - } -} - /* static */ inline bool isAlpha(const String8& string) { const size_t length = string.length(); @@ -230,8 +213,7 @@ void AaptLocaleValue::setVariant(const char* variantChars) { bool AaptLocaleValue::initFromFilterString(const String8& str) { // A locale (as specified in the filter) is an underscore separated name such // as "en_US", "en_Latn_US", or "en_US_POSIX". - Vector<String8> parts; - splitAndLowerCase(str.string(), &parts, '_'); + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_'); const int numTags = parts.size(); bool valid = false; @@ -301,8 +283,7 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta if (part[0] == 'b' && part[1] == '+') { // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags, // except that the separator is "+" and not "-". - Vector<String8> subtags; - AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+'); + Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+'); subtags.removeItemsAt(0); if (subtags.size() == 1) { setLanguage(subtags[0]); @@ -438,1349 +419,46 @@ void AaptLocaleValue::writeTo(ResTable_config* out) const { } } - -/* static */ bool -AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value) -{ - ResTable_config config; - memset(&config, 0, sizeof(ResTable_config)); - - // IMSI - MCC - if (getMccName(part.string(), &config)) { - *axis = AXIS_MCC; - value->intValue = config.mcc; - return true; - } - - // IMSI - MNC - if (getMncName(part.string(), &config)) { - *axis = AXIS_MNC; - value->intValue = config.mnc; - return true; - } - - // locale - language - if (value->localeValue.initFromFilterString(part)) { - *axis = AXIS_LOCALE; - return true; - } - - // layout direction - if (getLayoutDirectionName(part.string(), &config)) { - *axis = AXIS_LAYOUTDIR; - value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); - return true; - } - - // smallest screen dp width - if (getSmallestScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SMALLESTSCREENWIDTHDP; - value->intValue = config.smallestScreenWidthDp; - return true; - } - - // screen dp width - if (getScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SCREENWIDTHDP; - value->intValue = config.screenWidthDp; - return true; - } - - // screen dp height - if (getScreenHeightDpName(part.string(), &config)) { - *axis = AXIS_SCREENHEIGHTDP; - value->intValue = config.screenHeightDp; - return true; - } - - // screen layout size - if (getScreenLayoutSizeName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTSIZE; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); - return true; - } - - // screen layout long - if (getScreenLayoutLongName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTLONG; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG); - return true; - } - - // orientation - if (getOrientationName(part.string(), &config)) { - *axis = AXIS_ORIENTATION; - value->intValue = config.orientation; - return true; - } - - // ui mode type - if (getUiModeTypeName(part.string(), &config)) { - *axis = AXIS_UIMODETYPE; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - return true; - } - - // ui mode night - if (getUiModeNightName(part.string(), &config)) { - *axis = AXIS_UIMODENIGHT; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - return true; - } - - // density - if (getDensityName(part.string(), &config)) { - *axis = AXIS_DENSITY; - value->intValue = config.density; - return true; - } - - // touchscreen - if (getTouchscreenName(part.string(), &config)) { - *axis = AXIS_TOUCHSCREEN; - value->intValue = config.touchscreen; - return true; - } - - // keyboard hidden - if (getKeysHiddenName(part.string(), &config)) { - *axis = AXIS_KEYSHIDDEN; - value->intValue = config.inputFlags; - return true; - } - - // keyboard - if (getKeyboardName(part.string(), &config)) { - *axis = AXIS_KEYBOARD; - value->intValue = config.keyboard; - return true; - } - - // navigation hidden - if (getNavHiddenName(part.string(), &config)) { - *axis = AXIS_NAVHIDDEN; - value->intValue = config.inputFlags; - return 0; - } - - // navigation - if (getNavigationName(part.string(), &config)) { - *axis = AXIS_NAVIGATION; - value->intValue = config.navigation; - return true; - } - - // screen size - if (getScreenSizeName(part.string(), &config)) { - *axis = AXIS_SCREENSIZE; - value->intValue = config.screenSize; - return true; - } - - // version - if (getVersionName(part.string(), &config)) { - *axis = AXIS_VERSION; - value->intValue = config.version; - return true; - } - - return false; -} - -AxisValue -AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) -{ - AxisValue value; - switch (axis) { - case AXIS_MCC: - value.intValue = config.mcc; - break; - case AXIS_MNC: - value.intValue = config.mnc; - break; - case AXIS_LOCALE: - value.localeValue.initFromResTable(config); - break; - case AXIS_LAYOUTDIR: - value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR; - break; - case AXIS_SCREENLAYOUTSIZE: - value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE; - break; - case AXIS_ORIENTATION: - value.intValue = config.orientation; - break; - case AXIS_UIMODETYPE: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - break; - case AXIS_UIMODENIGHT: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - break; - case AXIS_DENSITY: - value.intValue = config.density; - break; - case AXIS_TOUCHSCREEN: - value.intValue = config.touchscreen; - break; - case AXIS_KEYSHIDDEN: - value.intValue = config.inputFlags; - break; - case AXIS_KEYBOARD: - value.intValue = config.keyboard; - break; - case AXIS_NAVIGATION: - value.intValue = config.navigation; - break; - case AXIS_SCREENSIZE: - value.intValue = config.screenSize; - break; - case AXIS_SMALLESTSCREENWIDTHDP: - value.intValue = config.smallestScreenWidthDp; - break; - case AXIS_SCREENWIDTHDP: - value.intValue = config.screenWidthDp; - break; - case AXIS_SCREENHEIGHTDP: - value.intValue = config.screenHeightDp; - break; - case AXIS_VERSION: - value.intValue = config.version; - break; - } - - return value; -} - -bool -AaptGroupEntry::configSameExcept(const ResTable_config& config, - const ResTable_config& otherConfig, int axis) -{ - for (int i=AXIS_START; i<=AXIS_END; i++) { - if (i == axis) { - continue; - } - if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) { - return false; - } - } - return true; -} - bool AaptGroupEntry::initFromDirName(const char* dir, String8* resType) { - mParamsChanged = true; - - Vector<String8> parts; - AaptLocaleValue::splitAndLowerCase(dir, &parts, '-'); - - String8 mcc, mnc, layoutsize, layoutlong, orient, den; - String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers; - String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; - - AaptLocaleValue locale; - int numLocaleComponents = 0; - - const int N = parts.size(); - int index = 0; - String8 part = parts[index]; - - // resource type - if (!isValidResourceType(part)) { - return false; - } - *resType = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - - // imsi - mcc - if (getMccName(part.string())) { - mcc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not mcc: %s\n", part.string()); - } - - // imsi - mnc - if (getMncName(part.string())) { - mnc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; + const char* q = strchr(dir, '-'); + size_t typeLen; + if (q != NULL) { + typeLen = q - dir; } else { - //printf("not mnc: %s\n", part.string()); + typeLen = strlen(dir); } - index = locale.initFromDirName(parts, index); - if (index == -1) { + String8 type(dir, typeLen); + if (!isValidResourceType(type)) { return false; } - if (index >= N){ - goto success; - } - - part = parts[index]; - if (getLayoutDirectionName(part.string())) { - layoutDir = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not layout direction: %s\n", part.string()); - } - - if (getSmallestScreenWidthDpName(part.string())) { - smallestwidthdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not smallest screen width dp: %s\n", part.string()); - } - - if (getScreenWidthDpName(part.string())) { - widthdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen width dp: %s\n", part.string()); - } - - if (getScreenHeightDpName(part.string())) { - heightdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen height dp: %s\n", part.string()); - } - - if (getScreenLayoutSizeName(part.string())) { - layoutsize = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout size: %s\n", part.string()); - } - - if (getScreenLayoutLongName(part.string())) { - layoutlong = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout long: %s\n", part.string()); - } - - // orientation - if (getOrientationName(part.string())) { - orient = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not orientation: %s\n", part.string()); - } - - // ui mode type - if (getUiModeTypeName(part.string())) { - uiModeType = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not ui mode type: %s\n", part.string()); - } - - // ui mode night - if (getUiModeNightName(part.string())) { - uiModeNight = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not ui mode night: %s\n", part.string()); - } - - // density - if (getDensityName(part.string())) { - den = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not density: %s\n", part.string()); - } - - // touchscreen - if (getTouchscreenName(part.string())) { - touch = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not touchscreen: %s\n", part.string()); - } - - // keyboard hidden - if (getKeysHiddenName(part.string())) { - keysHidden = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not keysHidden: %s\n", part.string()); - } - // keyboard - if (getKeyboardName(part.string())) { - key = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not keyboard: %s\n", part.string()); - } - - // navigation hidden - if (getNavHiddenName(part.string())) { - navHidden = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not navHidden: %s\n", part.string()); - } - - if (getNavigationName(part.string())) { - nav = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not navigation: %s\n", part.string()); - } - - if (getScreenSizeName(part.string())) { - size = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen size: %s\n", part.string()); - } - - if (getVersionName(part.string())) { - vers = part; - - index++; - if (index == N) { - goto success; + if (q != NULL) { + if (!AaptConfig::parse(String8(q + 1), &mParams)) { + return false; } - part = parts[index]; - } else { - //printf("not version: %s\n", part.string()); } - // if there are extra parts, it doesn't match - return false; - -success: - this->mcc = mcc; - this->mnc = mnc; - this->locale = locale; - this->screenLayoutSize = layoutsize; - this->screenLayoutLong = layoutlong; - this->smallestScreenWidthDp = smallestwidthdp; - this->screenWidthDp = widthdp; - this->screenHeightDp = heightdp; - this->orientation = orient; - this->uiModeType = uiModeType; - this->uiModeNight = uiModeNight; - this->density = den; - this->touchscreen = touch; - this->keysHidden = keysHidden; - this->keyboard = key; - this->navHidden = navHidden; - this->navigation = nav; - this->screenSize = size; - this->layoutDirection = layoutDir; - this->version = vers; - - // what is this anyway? - this->vendor = ""; - + *resType = type; return true; } String8 -AaptGroupEntry::toString() const -{ - String8 s = this->mcc; - s += ","; - s += this->mnc; - s += ","; - s += locale.toDirName(); - s += ","; - s += layoutDirection; - s += ","; - s += smallestScreenWidthDp; - s += ","; - s += screenWidthDp; - s += ","; - s += screenHeightDp; - s += ","; - s += screenLayoutSize; - s += ","; - s += screenLayoutLong; - s += ","; - s += this->orientation; - s += ","; - s += uiModeType; - s += ","; - s += uiModeNight; - s += ","; - s += density; - s += ","; - s += touchscreen; - s += ","; - s += keysHidden; - s += ","; - s += keyboard; - s += ","; - s += navHidden; - s += ","; - s += navigation; - s += ","; - s += screenSize; - s += ","; - s += version; - return s; -} - -String8 AaptGroupEntry::toDirName(const String8& resType) const { String8 s = resType; - if (this->mcc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mcc; - } - if (this->mnc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mnc; - } - - const String8 localeComponent = locale.toDirName(); - if (localeComponent != "") { - if (s.length() > 0) { - s += "-"; - } - s += localeComponent; - } - - if (this->layoutDirection != "") { - if (s.length() > 0) { - s += "-"; - } - s += layoutDirection; - } - if (this->smallestScreenWidthDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += smallestScreenWidthDp; - } - if (this->screenWidthDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenWidthDp; - } - if (this->screenHeightDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenHeightDp; - } - if (this->screenLayoutSize != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutSize; - } - if (this->screenLayoutLong != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutLong; - } - if (this->orientation != "") { - if (s.length() > 0) { - s += "-"; - } - s += orientation; - } - if (this->uiModeType != "") { - if (s.length() > 0) { - s += "-"; - } - s += uiModeType; - } - if (this->uiModeNight != "") { - if (s.length() > 0) { - s += "-"; - } - s += uiModeNight; - } - if (this->density != "") { - if (s.length() > 0) { - s += "-"; - } - s += density; - } - if (this->touchscreen != "") { - if (s.length() > 0) { - s += "-"; - } - s += touchscreen; - } - if (this->keysHidden != "") { - if (s.length() > 0) { - s += "-"; - } - s += keysHidden; - } - if (this->keyboard != "") { - if (s.length() > 0) { - s += "-"; - } - s += keyboard; - } - if (this->navHidden != "") { + String8 params = mParams.toString(); + if (params.length() > 0) { if (s.length() > 0) { s += "-"; } - s += navHidden; + s += params; } - if (this->navigation != "") { - if (s.length() > 0) { - s += "-"; - } - s += navigation; - } - if (this->screenSize != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenSize; - } - if (this->version != "") { - if (s.length() > 0) { - s += "-"; - } - s += version; - } - return s; } -bool AaptGroupEntry::getMccName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val != 3) return false; - - int d = atoi(val); - if (d != 0) { - if (out) out->mcc = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getMncName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'n') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val == 0 || c-val > 3) return false; - - if (out) { - out->mnc = atoi(val); - if (out->mnc == 0) { - out->mnc = ACONFIGURATION_MNC_ZERO; - } - } - - return true; -} - -bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_ANY; - return true; - } else if (strcmp(name, "ldltr") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_LTR; - return true; - } else if (strcmp(name, "ldrtl") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_RTL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_ANY; - return true; - } else if (strcmp(name, "small") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_SMALL; - return true; - } else if (strcmp(name, "normal") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_NORMAL; - return true; - } else if (strcmp(name, "large") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_LARGE; - return true; - } else if (strcmp(name, "xlarge") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_XLARGE; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenLayoutLongName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_ANY; - return true; - } else if (strcmp(name, "long") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_YES; - return true; - } else if (strcmp(name, "notlong") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_NO; - return true; - } - - return false; -} - -bool AaptGroupEntry::getOrientationName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->orientation = out->ORIENTATION_ANY; - return true; - } else if (strcmp(name, "port") == 0) { - if (out) out->orientation = out->ORIENTATION_PORT; - return true; - } else if (strcmp(name, "land") == 0) { - if (out) out->orientation = out->ORIENTATION_LAND; - return true; - } else if (strcmp(name, "square") == 0) { - if (out) out->orientation = out->ORIENTATION_SQUARE; - return true; - } - - return false; -} - -bool AaptGroupEntry::getUiModeTypeName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_ANY; - return true; - } else if (strcmp(name, "desk") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_DESK; - return true; - } else if (strcmp(name, "car") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_CAR; - return true; - } else if (strcmp(name, "television") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_TELEVISION; - return true; - } else if (strcmp(name, "appliance") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_APPLIANCE; - return true; - } else if (strcmp(name, "watch") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_WATCH; - return true; - } - - return false; -} - -bool AaptGroupEntry::getUiModeNightName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_ANY; - return true; - } else if (strcmp(name, "night") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_YES; - return true; - } else if (strcmp(name, "notnight") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_NO; - return true; - } - - return false; -} - -bool AaptGroupEntry::getDensityName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->density = ResTable_config::DENSITY_DEFAULT; - return true; - } - - if (strcmp(name, "nodpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_NONE; - return true; - } - - if (strcmp(name, "ldpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_LOW; - return true; - } - - if (strcmp(name, "mdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_MEDIUM; - return true; - } - - if (strcmp(name, "tvdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_TV; - return true; - } - - if (strcmp(name, "hdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_HIGH; - return true; - } - - if (strcmp(name, "xhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XHIGH; - return true; - } - - if (strcmp(name, "xxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXHIGH; - return true; - } - - if (strcmp(name, "xxxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXXHIGH; - return true; - } - - char* c = (char*)name; - while (*c >= '0' && *c <= '9') { - c++; - } - - // check that we have 'dpi' after the last digit. - if (toupper(c[0]) != 'D' || - toupper(c[1]) != 'P' || - toupper(c[2]) != 'I' || - c[3] != 0) { - return false; - } - - // temporarily replace the first letter with \0 to - // use atoi. - char tmp = c[0]; - c[0] = '\0'; - - int d = atoi(name); - c[0] = tmp; - - if (d != 0) { - if (out) out->density = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getTouchscreenName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_ANY; - return true; - } else if (strcmp(name, "notouch") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; - return true; - } else if (strcmp(name, "stylus") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; - return true; - } else if (strcmp(name, "finger") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeysHiddenName(const char* name, - ResTable_config* out) -{ - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_ANY; - } else if (strcmp(name, "keysexposed") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_NO; - } else if (strcmp(name, "keyshidden") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_YES; - } else if (strcmp(name, "keyssoft") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_SOFT; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags&~mask) | value; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeyboardName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->keyboard = out->KEYBOARD_ANY; - return true; - } else if (strcmp(name, "nokeys") == 0) { - if (out) out->keyboard = out->KEYBOARD_NOKEYS; - return true; - } else if (strcmp(name, "qwerty") == 0) { - if (out) out->keyboard = out->KEYBOARD_QWERTY; - return true; - } else if (strcmp(name, "12key") == 0) { - if (out) out->keyboard = out->KEYBOARD_12KEY; - return true; - } - - return false; -} - -bool AaptGroupEntry::getNavHiddenName(const char* name, - ResTable_config* out) -{ - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_ANY; - } else if (strcmp(name, "navexposed") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_NO; - } else if (strcmp(name, "navhidden") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_YES; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags&~mask) | value; - return true; - } - - return false; -} - -bool AaptGroupEntry::getNavigationName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->navigation = out->NAVIGATION_ANY; - return true; - } else if (strcmp(name, "nonav") == 0) { - if (out) out->navigation = out->NAVIGATION_NONAV; - return true; - } else if (strcmp(name, "dpad") == 0) { - if (out) out->navigation = out->NAVIGATION_DPAD; - return true; - } else if (strcmp(name, "trackball") == 0) { - if (out) out->navigation = out->NAVIGATION_TRACKBALL; - return true; - } else if (strcmp(name, "wheel") == 0) { - if (out) out->navigation = out->NAVIGATION_WHEEL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidth = out->SCREENWIDTH_ANY; - out->screenHeight = out->SCREENHEIGHT_ANY; - } - return true; - } - - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || *x != 'x') return false; - String8 xName(name, x-name); - x++; - - const char* y = x; - while (*y >= '0' && *y <= '9') y++; - if (y == name || *y != 0) return false; - String8 yName(x, y-x); - - uint16_t w = (uint16_t)atoi(xName.string()); - uint16_t h = (uint16_t)atoi(yName.string()); - if (w < h) { - return false; - } - - if (out) { - out->screenWidth = w; - out->screenHeight = h; - } - - return true; -} - -bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 's') return false; - name++; - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->screenWidthDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenHeightDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'h') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->screenHeightDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->sdkVersion = out->SDKVERSION_ANY; - out->minorVersion = out->MINORVERSION_ANY; - } - return true; - } - - if (*name != 'v') { - return false; - } - - name++; - const char* s = name; - while (*s >= '0' && *s <= '9') s++; - if (s == name || *s != 0) return false; - String8 sdkName(name, s-name); - - if (out) { - out->sdkVersion = (uint16_t)atoi(sdkName.string()); - out->minorVersion = 0; - } - - return true; -} - -int AaptGroupEntry::compare(const AaptGroupEntry& o) const -{ - int v = mcc.compare(o.mcc); - if (v == 0) v = mnc.compare(o.mnc); - if (v == 0) v = locale.compare(o.locale); - if (v == 0) v = layoutDirection.compare(o.layoutDirection); - if (v == 0) v = vendor.compare(o.vendor); - if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); - if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); - if (v == 0) v = screenHeightDp.compare(o.screenHeightDp); - if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); - if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); - if (v == 0) v = orientation.compare(o.orientation); - if (v == 0) v = uiModeType.compare(o.uiModeType); - if (v == 0) v = uiModeNight.compare(o.uiModeNight); - if (v == 0) v = density.compare(o.density); - if (v == 0) v = touchscreen.compare(o.touchscreen); - if (v == 0) v = keysHidden.compare(o.keysHidden); - if (v == 0) v = keyboard.compare(o.keyboard); - if (v == 0) v = navHidden.compare(o.navHidden); - if (v == 0) v = navigation.compare(o.navigation); - if (v == 0) v = screenSize.compare(o.screenSize); - if (v == 0) v = version.compare(o.version); - return v; -} - -const ResTable_config AaptGroupEntry::toParams() const -{ - if (!mParamsChanged) { - return mParams; - } - - mParamsChanged = false; - ResTable_config& params = mParams; - memset(¶ms, 0, sizeof(ResTable_config)); - getMccName(mcc.string(), ¶ms); - getMncName(mnc.string(), ¶ms); - locale.writeTo(¶ms); - getLayoutDirectionName(layoutDirection.string(), ¶ms); - getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); - getScreenWidthDpName(screenWidthDp.string(), ¶ms); - getScreenHeightDpName(screenHeightDp.string(), ¶ms); - getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); - getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); - getOrientationName(orientation.string(), ¶ms); - getUiModeTypeName(uiModeType.string(), ¶ms); - getUiModeNightName(uiModeNight.string(), ¶ms); - getDensityName(density.string(), ¶ms); - getTouchscreenName(touchscreen.string(), ¶ms); - getKeysHiddenName(keysHidden.string(), ¶ms); - getKeyboardName(keyboard.string(), ¶ms); - getNavHiddenName(navHidden.string(), ¶ms); - getNavigationName(navigation.string(), ¶ms); - getScreenSizeName(screenSize.string(), ¶ms); - getVersionName(version.string(), ¶ms); - - // Fix up version number based on specified parameters. - int minSdk = 0; - if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY - || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY - || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { - minSdk = SDK_HONEYCOMB_MR2; - } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) - != ResTable_config::UI_MODE_TYPE_ANY - || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) - != ResTable_config::UI_MODE_NIGHT_ANY) { - minSdk = SDK_FROYO; - } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) - != ResTable_config::SCREENSIZE_ANY - || (params.screenLayout&ResTable_config::MASK_SCREENLONG) - != ResTable_config::SCREENLONG_ANY - || params.density != ResTable_config::DENSITY_DEFAULT) { - minSdk = SDK_DONUT; - } - - if (minSdk > params.sdkVersion) { - params.sdkVersion = minSdk; - } - - return params; -} // ========================================================================= // ========================================================================= @@ -2229,9 +907,7 @@ AaptAssets::AaptAssets() : AaptDir(String8(), String8()), mHavePrivateSymbols(false), mChanged(false), mHaveIncludedAssets(false), - mRes(NULL) -{ -} + mRes(NULL) {} const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { if (mChanged) { @@ -2506,7 +1182,7 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) String8 resType; bool b = group.initFromDirName(entry->d_name, &resType); if (!b) { - fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), + fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(), entry->d_name); err = -1; continue; @@ -2654,30 +1330,35 @@ bail: status_t AaptAssets::filter(Bundle* bundle) { - ResourceFilter reqFilter; + WeakResourceFilter reqFilter; status_t err = reqFilter.parse(bundle->getConfigurations()); if (err != NO_ERROR) { return err; } - ResourceFilter prefFilter; - err = prefFilter.parse(bundle->getPreferredConfigurations()); - if (err != NO_ERROR) { - return err; + uint32_t preferredDensity = 0; + if (bundle->getPreferredDensity().size() > 0) { + ResTable_config preferredConfig; + if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) { + fprintf(stderr, "Error parsing preferred density: %s\n", + bundle->getPreferredDensity().string()); + return UNKNOWN_ERROR; + } + preferredDensity = preferredConfig.density; } - if (reqFilter.isEmpty() && prefFilter.isEmpty()) { + if (reqFilter.isEmpty() && preferredDensity == 0) { return NO_ERROR; } if (bundle->getVerbose()) { if (!reqFilter.isEmpty()) { printf("Applying required filter: %s\n", - bundle->getConfigurations()); + bundle->getConfigurations().string()); } - if (!prefFilter.isEmpty()) { - printf("Applying preferred filter: %s\n", - bundle->getPreferredConfigurations()); + if (preferredDensity > 0) { + printf("Applying preferred density filter: %s\n", + bundle->getPreferredDensity().string()); } } @@ -2734,88 +1415,70 @@ status_t AaptAssets::filter(Bundle* bundle) } // Quick check: no preferred filters, nothing more to do. - if (prefFilter.isEmpty()) { + if (preferredDensity == 0) { continue; } // Get the preferred density if there is one. We do not match exactly for density. // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we // pick xhdpi. - uint32_t preferredDensity = 0; - const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); - if (preferredConfigs != NULL && preferredConfigs->size() > 0) { - preferredDensity = (*preferredConfigs)[0].intValue; - } + for (size_t k=0; k<grp->getFiles().size(); k++) { + sp<AaptFile> file = grp->getFiles().valueAt(k); + if (k == 0 && grp->getFiles().size() == 1) { + // If this is the only file left, we need to keep it. + // Otherwise the resource IDs we are using will be inconsistent + // with what we get when not stripping. Sucky, but at least + // for now we can rely on the back-end doing another filtering + // pass to take this out and leave us with this resource name + // containing no entries. + continue; + } + if (file->getPath().getPathExtension() == ".xml") { + // We can't remove .xml files at this point, because when + // we parse them they may add identifier resources, so + // removing them can cause our resource identifiers to + // become inconsistent. + continue; + } + const ResTable_config& config(file->getGroupEntry().toParams()); + if (config.density != 0 && config.density != preferredDensity) { + // This is a resource we would prefer not to have. Check + // to see if have a similar variation that we would like + // to have and, if so, we can drop it. + uint32_t bestDensity = config.density; + + for (size_t m=0; m<grp->getFiles().size(); m++) { + if (m == k) { + continue; + } - // Now deal with preferred configurations. - for (int axis=AXIS_START; axis<=AXIS_END; axis++) { - for (size_t k=0; k<grp->getFiles().size(); k++) { - sp<AaptFile> file = grp->getFiles().valueAt(k); - if (k == 0 && grp->getFiles().size() == 1) { - // If this is the only file left, we need to keep it. - // Otherwise the resource IDs we are using will be inconsistent - // with what we get when not stripping. Sucky, but at least - // for now we can rely on the back-end doing another filtering - // pass to take this out and leave us with this resource name - // containing no entries. - continue; - } - if (file->getPath().getPathExtension() == ".xml") { - // We can't remove .xml files at this point, because when - // we parse them they may add identifier resources, so - // removing them can cause our resource identifiers to - // become inconsistent. - continue; - } - const ResTable_config& config(file->getGroupEntry().toParams()); - if (!prefFilter.match(axis, config)) { - // This is a resource we would prefer not to have. Check - // to see if have a similar variation that we would like - // to have and, if so, we can drop it. - - uint32_t bestDensity = config.density; - - for (size_t m=0; m<grp->getFiles().size(); m++) { - if (m == k) continue; - sp<AaptFile> mfile = grp->getFiles().valueAt(m); - const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); - if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) { - if (axis == AXIS_DENSITY && preferredDensity > 0) { - // See if there is a better density resource - if (mconfig.density < bestDensity && - mconfig.density > preferredDensity && - bestDensity > preferredDensity) { - // This density is between our best density and - // the preferred density, therefore it is better. - bestDensity = mconfig.density; - } else if (mconfig.density > bestDensity && - bestDensity < preferredDensity) { - // This density is better than our best density and - // our best density was smaller than our preferred - // density, so it is better. - bestDensity = mconfig.density; - } - } else if (prefFilter.match(axis, mconfig)) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; - break; - } + sp<AaptFile> mfile = grp->getFiles().valueAt(m); + const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); + if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) { + // See if there is a better density resource + if (mconfig.density < bestDensity && + mconfig.density > preferredDensity && + bestDensity > preferredDensity) { + // This density is between our best density and + // the preferred density, therefore it is better. + bestDensity = mconfig.density; + } else if (mconfig.density > bestDensity && + bestDensity < preferredDensity) { + // This density is better than our best density and + // our best density was smaller than our preferred + // density, so it is better. + bestDensity = mconfig.density; } } + } - if (axis == AXIS_DENSITY && preferredDensity > 0 && - bestDensity != config.density) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; + if (bestDensity != config.density) { + if (bundle->getVerbose()) { + printf("Pruning unneeded resource: %s\n", + file->getPrintableSource().string()); } + grp->removeFile(k); + k--; } } } diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 82dda5f..0c2576a 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -6,22 +6,24 @@ #ifndef __AAPT_ASSETS_H #define __AAPT_ASSETS_H -#include <stdlib.h> #include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> +#include <stdlib.h> +#include <set> #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> #include <utils/Vector.h> -#include "ZipFile.h" +#include "AaptConfig.h" #include "Bundle.h" +#include "ConfigDescription.h" #include "SourcePos.h" +#include "ZipFile.h" using namespace android; - extern const char * const gDefaultIgnoreAssets; extern const char * gUserIgnoreAssets; @@ -82,9 +84,6 @@ struct AaptLocaleValue { return memcmp(this, &other, sizeof(AaptLocaleValue)); } - static void splitAndLowerCase(const char* const chars, Vector<String8>* parts, - const char separator); - inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; } inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; } inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; } @@ -98,31 +97,6 @@ private: void setVariant(const char* variant); }; -struct AxisValue { - // Used for all axes except AXIS_LOCALE, which is represented - // as a AaptLocaleValue value. - int intValue; - AaptLocaleValue localeValue; - - AxisValue() : intValue(0) { - } - - inline int compare(const AxisValue &other) const { - if (intValue != other.intValue) { - return intValue - other.intValue; - } - - return localeValue.compare(other.localeValue); - } - - inline bool operator<(const AxisValue& o) const { return compare(o) < 0; } - inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; } - inline bool operator==(const AxisValue& o) const { return compare(o) == 0; } - inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; } - inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; } - inline bool operator>(const AxisValue& o) const { return compare(o) > 0; } -}; - /** * This structure contains a specific variation of a single file out * of all the variations it can have that we can have. @@ -130,23 +104,11 @@ struct AxisValue { struct AaptGroupEntry { public: - AaptGroupEntry() : mParamsChanged(true) { - memset(&mParams, 0, sizeof(ResTable_config)); - } - bool initFromDirName(const char* dir, String8* resType); - static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value); - - static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis); - - static bool configSameExcept(const ResTable_config& config, - const ResTable_config& otherConfig, int axis); - - int compare(const AaptGroupEntry& o) const; - - const ResTable_config toParams() const; + inline const ConfigDescription& toParams() const { return mParams; } + inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); } inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; } inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; } inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; } @@ -154,56 +116,13 @@ public: inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; } inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; } - String8 toString() const; + String8 toString() const { return mParams.toString(); } String8 toDirName(const String8& resType) const; - const String8& getVersionString() const { return version; } + const String8 getVersionString() const { return AaptConfig::getVersion(mParams); } private: - static bool getMccName(const char* name, ResTable_config* out = NULL); - static bool getMncName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL); - static bool getOrientationName(const char* name, ResTable_config* out = NULL); - static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL); - static bool getUiModeNightName(const char* name, ResTable_config* out = NULL); - static bool getDensityName(const char* name, ResTable_config* out = NULL); - static bool getTouchscreenName(const char* name, ResTable_config* out = NULL); - static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL); - static bool getKeyboardName(const char* name, ResTable_config* out = NULL); - static bool getNavigationName(const char* name, ResTable_config* out = NULL); - static bool getNavHiddenName(const char* name, ResTable_config* out = NULL); - static bool getScreenSizeName(const char* name, ResTable_config* out = NULL); - static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL); - static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL); - static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL); - static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL); - static bool getVersionName(const char* name, ResTable_config* out = NULL); - - String8 mcc; - String8 mnc; - AaptLocaleValue locale; - String8 vendor; - String8 smallestScreenWidthDp; - String8 screenWidthDp; - String8 screenHeightDp; - String8 screenLayoutSize; - String8 screenLayoutLong; - String8 orientation; - String8 uiModeType; - String8 uiModeNight; - String8 density; - String8 touchscreen; - String8 keysHidden; - String8 keyboard; - String8 navHidden; - String8 navigation; - String8 screenSize; - String8 layoutDirection; - String8 version; - - mutable bool mParamsChanged; - mutable ResTable_config mParams; + ConfigDescription mParams; }; inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs) diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp new file mode 100644 index 0000000..69a9c7f --- /dev/null +++ b/tools/aapt/AaptConfig.cpp @@ -0,0 +1,790 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <ctype.h> + +#include "AaptConfig.h" +#include "AaptAssets.h" +#include "AaptUtil.h" +#include "ResourceFilter.h" + +using android::String8; +using android::Vector; +using android::ResTable_config; + +namespace AaptConfig { + +static const char* kWildcardName = "any"; + +bool parse(const String8& str, ConfigDescription* out) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-'); + + ConfigDescription config; + AaptLocaleValue locale; + ssize_t index = 0; + ssize_t localeIndex = 0; + const ssize_t N = parts.size(); + const char* part = parts[index].string(); + + if (str.length() == 0) { + goto success; + } + + if (parseMcc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseMnc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Locale spans a few '-' separators, so we let it + // control the index. + localeIndex = locale.initFromDirName(parts, index); + if (localeIndex < 0) { + return false; + } else if (localeIndex > index) { + locale.writeTo(&config); + index = localeIndex; + if (index >= N) { + goto success; + } + part = parts[index].string(); + } + + if (parseLayoutDirection(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseSmallestScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenHeightDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutLong(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseOrientation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeType(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeNight(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseDensity(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseTouchscreen(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeysHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeyboard(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavigation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseVersion(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Unrecognized. + return false; + +success: + if (out != NULL) { + applyVersionForCompatibility(&config); + *out = config; + } + return true; +} + +bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ','); + const size_t N = parts.size(); + for (size_t i = 0; i < N; i++) { + ConfigDescription config; + if (!parse(parts[i], &config)) { + return false; + } + outSet->insert(config); + } + return true; +} + +void applyVersionForCompatibility(ConfigDescription* config) { + if (config == NULL) { + return; + } + + uint16_t minSdk = 0; + if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { + minSdk = SDK_HONEYCOMB_MR2; + } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) + != ResTable_config::UI_MODE_TYPE_ANY + || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) + != ResTable_config::UI_MODE_NIGHT_ANY) { + minSdk = SDK_FROYO; + } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) + != ResTable_config::SCREENSIZE_ANY + || (config->screenLayout & ResTable_config::MASK_SCREENLONG) + != ResTable_config::SCREENLONG_ANY + || config->density != ResTable_config::DENSITY_DEFAULT) { + minSdk = SDK_DONUT; + } + + if (minSdk > config->sdkVersion) { + config->sdkVersion = minSdk; + } +} + +bool parseMcc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val != 3) return false; + + int d = atoi(val); + if (d != 0) { + if (out) out->mcc = d; + return true; + } + + return false; +} + +bool parseMnc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'n') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val == 0 || c-val > 3) return false; + + if (out) { + out->mnc = atoi(val); + if (out->mnc == 0) { + out->mnc = ACONFIGURATION_MNC_ZERO; + } + } + + return true; +} + +bool parseLayoutDirection(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_ANY; + return true; + } else if (strcmp(name, "ldltr") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_LTR; + return true; + } else if (strcmp(name, "ldrtl") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_RTL; + return true; + } + + return false; +} + +bool parseScreenLayoutSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_ANY; + return true; + } else if (strcmp(name, "small") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_SMALL; + return true; + } else if (strcmp(name, "normal") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_NORMAL; + return true; + } else if (strcmp(name, "large") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_LARGE; + return true; + } else if (strcmp(name, "xlarge") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_XLARGE; + return true; + } + + return false; +} + +bool parseScreenLayoutLong(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_ANY; + return true; + } else if (strcmp(name, "long") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_YES; + return true; + } else if (strcmp(name, "notlong") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_NO; + return true; + } + + return false; +} + +bool parseOrientation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->orientation = out->ORIENTATION_ANY; + return true; + } else if (strcmp(name, "port") == 0) { + if (out) out->orientation = out->ORIENTATION_PORT; + return true; + } else if (strcmp(name, "land") == 0) { + if (out) out->orientation = out->ORIENTATION_LAND; + return true; + } else if (strcmp(name, "square") == 0) { + if (out) out->orientation = out->ORIENTATION_SQUARE; + return true; + } + + return false; +} + +bool parseUiModeType(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_ANY; + return true; + } else if (strcmp(name, "desk") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_DESK; + return true; + } else if (strcmp(name, "car") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_CAR; + return true; + } else if (strcmp(name, "television") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_TELEVISION; + return true; + } else if (strcmp(name, "appliance") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_APPLIANCE; + return true; + } else if (strcmp(name, "watch") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_WATCH; + return true; + } + + return false; +} + +bool parseUiModeNight(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_ANY; + return true; + } else if (strcmp(name, "night") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_YES; + return true; + } else if (strcmp(name, "notnight") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_NO; + return true; + } + + return false; +} + +bool parseDensity(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->density = ResTable_config::DENSITY_DEFAULT; + return true; + } + + if (strcmp(name, "nodpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_NONE; + return true; + } + + if (strcmp(name, "ldpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_LOW; + return true; + } + + if (strcmp(name, "mdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_MEDIUM; + return true; + } + + if (strcmp(name, "tvdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_TV; + return true; + } + + if (strcmp(name, "hdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_HIGH; + return true; + } + + if (strcmp(name, "xhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XHIGH; + return true; + } + + if (strcmp(name, "xxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXHIGH; + return true; + } + + if (strcmp(name, "xxxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXXHIGH; + return true; + } + + char* c = (char*)name; + while (*c >= '0' && *c <= '9') { + c++; + } + + // check that we have 'dpi' after the last digit. + if (toupper(c[0]) != 'D' || + toupper(c[1]) != 'P' || + toupper(c[2]) != 'I' || + c[3] != 0) { + return false; + } + + // temporarily replace the first letter with \0 to + // use atoi. + char tmp = c[0]; + c[0] = '\0'; + + int d = atoi(name); + c[0] = tmp; + + if (d != 0) { + if (out) out->density = d; + return true; + } + + return false; +} + +bool parseTouchscreen(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_ANY; + return true; + } else if (strcmp(name, "notouch") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; + return true; + } else if (strcmp(name, "stylus") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; + return true; + } else if (strcmp(name, "finger") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; + return true; + } + + return false; +} + +bool parseKeysHidden(const char* name, ResTable_config* out) { + uint8_t mask = 0; + uint8_t value = 0; + if (strcmp(name, kWildcardName) == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_ANY; + } else if (strcmp(name, "keysexposed") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_NO; + } else if (strcmp(name, "keyshidden") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_YES; + } else if (strcmp(name, "keyssoft") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_SOFT; + } + + if (mask != 0) { + if (out) out->inputFlags = (out->inputFlags&~mask) | value; + return true; + } + + return false; +} + +bool parseKeyboard(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->keyboard = out->KEYBOARD_ANY; + return true; + } else if (strcmp(name, "nokeys") == 0) { + if (out) out->keyboard = out->KEYBOARD_NOKEYS; + return true; + } else if (strcmp(name, "qwerty") == 0) { + if (out) out->keyboard = out->KEYBOARD_QWERTY; + return true; + } else if (strcmp(name, "12key") == 0) { + if (out) out->keyboard = out->KEYBOARD_12KEY; + return true; + } + + return false; +} + +bool parseNavHidden(const char* name, ResTable_config* out) { + uint8_t mask = 0; + uint8_t value = 0; + if (strcmp(name, kWildcardName) == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_ANY; + } else if (strcmp(name, "navexposed") == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_NO; + } else if (strcmp(name, "navhidden") == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_YES; + } + + if (mask != 0) { + if (out) out->inputFlags = (out->inputFlags&~mask) | value; + return true; + } + + return false; +} + +bool parseNavigation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->navigation = out->NAVIGATION_ANY; + return true; + } else if (strcmp(name, "nonav") == 0) { + if (out) out->navigation = out->NAVIGATION_NONAV; + return true; + } else if (strcmp(name, "dpad") == 0) { + if (out) out->navigation = out->NAVIGATION_DPAD; + return true; + } else if (strcmp(name, "trackball") == 0) { + if (out) out->navigation = out->NAVIGATION_TRACKBALL; + return true; + } else if (strcmp(name, "wheel") == 0) { + if (out) out->navigation = out->NAVIGATION_WHEEL; + return true; + } + + return false; +} + +bool parseScreenSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenWidth = out->SCREENWIDTH_ANY; + out->screenHeight = out->SCREENHEIGHT_ANY; + } + return true; + } + + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || *x != 'x') return false; + String8 xName(name, x-name); + x++; + + const char* y = x; + while (*y >= '0' && *y <= '9') y++; + if (y == name || *y != 0) return false; + String8 yName(x, y-x); + + uint16_t w = (uint16_t)atoi(xName.string()); + uint16_t h = (uint16_t)atoi(yName.string()); + if (w < h) { + return false; + } + + if (out) { + out->screenWidth = w; + out->screenHeight = h; + } + + return true; +} + +bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 's') return false; + name++; + if (*name != 'w') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseScreenWidthDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenWidthDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 'w') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->screenWidthDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseScreenHeightDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenHeightDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 'h') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->screenHeightDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseVersion(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->sdkVersion = out->SDKVERSION_ANY; + out->minorVersion = out->MINORVERSION_ANY; + } + return true; + } + + if (*name != 'v') { + return false; + } + + name++; + const char* s = name; + while (*s >= '0' && *s <= '9') s++; + if (s == name || *s != 0) return false; + String8 sdkName(name, s-name); + + if (out) { + out->sdkVersion = (uint16_t)atoi(sdkName.string()); + out->minorVersion = 0; + } + + return true; +} + +String8 getVersion(const ResTable_config& config) { + return String8::format("v%u", config.sdkVersion); +} + +bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) { + return a.diff(b) == axisMask; +} + +} // namespace AaptConfig diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h new file mode 100644 index 0000000..2963539 --- /dev/null +++ b/tools/aapt/AaptConfig.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#ifndef __AAPT_CONFIG_H +#define __AAPT_CONFIG_H + +#include <set> +#include <utils/String8.h> + +#include "ConfigDescription.h" + +/** + * Utility methods for dealing with configurations. + */ +namespace AaptConfig { + +/** + * Parse a string of the form 'fr-sw600dp-land' and fill in the + * given ResTable_config with resulting configuration parameters. + * + * The resulting configuration has the appropriate sdkVersion defined + * for backwards compatibility. + */ +bool parse(const android::String8& str, ConfigDescription* out = NULL); + +/** + * Parse a comma separated list of configuration strings. Duplicate configurations + * will be removed. + * + * Example input: "fr,de-land,fr-sw600dp-land" + */ +bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet); + +/** + * If the configuration uses an axis that was added after + * the original Android release, make sure the SDK version + * is set accordingly. + */ +void applyVersionForCompatibility(ConfigDescription* config); + +// Individual axis +bool parseMcc(const char* str, android::ResTable_config* out = NULL); +bool parseMnc(const char* str, android::ResTable_config* out = NULL); +bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL); +bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL); +bool parseOrientation(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeType(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL); +bool parseDensity(const char* str, android::ResTable_config* out = NULL); +bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL); +bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL); +bool parseKeyboard(const char* str, android::ResTable_config* out = NULL); +bool parseNavHidden(const char* str, android::ResTable_config* out = NULL); +bool parseNavigation(const char* str, android::ResTable_config* out = NULL); +bool parseScreenSize(const char* str, android::ResTable_config* out = NULL); +bool parseVersion(const char* str, android::ResTable_config* out = NULL); + +android::String8 getVersion(const android::ResTable_config& config); + +/** + * Returns true if the two configurations only differ by the specified axis. + * The axis mask is a bitmask of CONFIG_* constants. + */ +bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask); + +} // namespace AaptConfig + +#endif // __AAPT_CONFIG_H diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp new file mode 100644 index 0000000..293e144 --- /dev/null +++ b/tools/aapt/AaptUtil.cpp @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#include "AaptUtil.h" + +using android::Vector; +using android::String8; + +namespace AaptUtil { + +Vector<String8> split(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + parts.add(String8(p, strlen(p))); + return parts; + } + + parts.add(String8(p, q-p)); + p = q + 1; + } + return parts; +} + +Vector<String8> splitAndLowerCase(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + String8 val(p, strlen(p)); + val.toLower(); + parts.add(val); + return parts; + } + + String8 val(p, q-p); + val.toLower(); + parts.add(val); + p = q + 1; + } + return parts; +} + +} // namespace AaptUtil diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h new file mode 100644 index 0000000..47a704a --- /dev/null +++ b/tools/aapt/AaptUtil.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef __AAPT_UTIL_H +#define __AAPT_UTIL_H + +#include <utils/String8.h> +#include <utils/Vector.h> + +namespace AaptUtil { + +android::Vector<android::String8> split(const android::String8& str, const char sep); +android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep); + +} // namespace AaptUtil + +#endif // __AAPT_UTIL_H diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 806f8ff..700afa1 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -1,104 +1,168 @@ -# -# Copyright 2006 The Android Open Source Project # -# Android Asset Packaging Tool +# 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. # # This tool is prebuilt if we're doing an app-only build. ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),) +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== +LOCAL_PATH:= $(call my-dir) -aapt_src_files := \ - AaptAssets.cpp \ - Command.cpp \ - CrunchCache.cpp \ - FileFinder.cpp \ - Main.cpp \ - Package.cpp \ - StringPool.cpp \ - XMLNode.cpp \ - ResourceFilter.cpp \ - ResourceIdCache.cpp \ - ResourceTable.cpp \ - Images.cpp \ - Resource.cpp \ +aaptMain := Main.cpp +aaptSources := \ + AaptAssets.cpp \ + AaptConfig.cpp \ + AaptUtil.cpp \ + ApkBuilder.cpp \ + Command.cpp \ + CrunchCache.cpp \ + FileFinder.cpp \ + Package.cpp \ + StringPool.cpp \ + XMLNode.cpp \ + ResourceFilter.cpp \ + ResourceIdCache.cpp \ + ResourceTable.cpp \ + Images.cpp \ + Resource.cpp \ pseudolocalize.cpp \ SourcePos.cpp \ - WorkQueue.cpp \ + WorkQueue.cpp \ ZipEntry.cpp \ ZipFile.cpp \ - qsort_r_compat.c + qsort_r_compat.c + +aaptTests := \ + tests/AaptConfig_test.cpp \ + tests/AaptGroupEntry_test.cpp \ + tests/ResourceFilter_test.cpp + +aaptCIncludes := \ + external/libpng \ + external/zlib + +aaptHostLdLibs := +aaptHostStaticLibs := \ + libandroidfw \ + libpng \ + liblog \ + libutils \ + libcutils \ + libexpat \ + libziparchive-host -LOCAL_PATH:= $(call my-dir) +ifeq ($(HOST_OS),linux) + aaptHostLdLibs += -lrt -ldl -lpthread +endif + +# Statically link libz for MinGW (Win SDK under Linux), +# and dynamically link for all others. +ifneq ($(strip $(USE_MINGW)),) + aaptHostStaticLibs += libz +else + aaptHostLdLibs += -lz +endif + + +# ========================================================== +# Build the host static library: libaapt +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt + +LOCAL_SRC_FILES := $(aaptSources) +LOCAL_C_INCLUDES += $(aaptCIncludes) LOCAL_CFLAGS += -Wno-format-y2k +LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS ifeq (darwin,$(HOST_OS)) LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS endif -LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS +include $(BUILD_HOST_STATIC_LIBRARY) -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib -LOCAL_STATIC_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libexpat \ - libpng \ - liblog \ - libziparchive-host +# ========================================================== +# Build the host executable: aapt +# ========================================================== +include $(CLEAR_VARS) -ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt -ldl -lpthread -endif +LOCAL_MODULE := aapt -# Statically link libz for MinGW (Win SDK under Linux), -# and dynamically link for all others. -ifneq ($(strip $(USE_MINGW)),) - LOCAL_STATIC_LIBRARIES += libz -else - LOCAL_LDLIBS += -lz -endif +LOCAL_SRC_FILES := $(aaptMain) -LOCAL_MODULE := aapt +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) include $(BUILD_HOST_EXECUTABLE) -# aapt for running on the device -# ========================================================= -ifneq ($(SDK_ONLY),true) + +# ========================================================== +# Build the host tests: libaapt_tests +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt_tests -LOCAL_MODULE := aapt +LOCAL_SRC_FILES += $(aaptTests) +LOCAL_C_INCLUDES += $(LOCAL_PATH) -LOCAL_C_INCLUDES += bionic -LOCAL_C_INCLUDES += bionic/libstdc++/include -LOCAL_C_INCLUDES += external/stlport/stlport -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) -LOCAL_CFLAGS += -Wno-non-virtual-dtor +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device executable: aapt +# ========================================================== +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_MODULE := aapt + +LOCAL_SRC_FILES := $(aaptSources) $(aaptMain) +LOCAL_C_INCLUDES += \ + $(aaptCIncludes) \ + bionic \ + external/stlport/stlport LOCAL_SHARED_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libpng \ - liblog \ - libz + libandroidfw \ + libutils \ + libcutils \ + libpng \ + liblog \ + libz LOCAL_STATIC_LIBRARIES := \ - libstlport_static \ - libexpat_static + libstlport_static \ + libexpat_static + +LOCAL_CPPFLAGS += -Wno-non-virtual-dtor include $(BUILD_EXECUTABLE) -endif + +endif # Not SDK_ONLY endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp new file mode 100644 index 0000000..12f6040 --- /dev/null +++ b/tools/aapt/ApkBuilder.cpp @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include "AaptAssets.h" +#include "ApkBuilder.h" + +using namespace android; + +ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter) + : mConfigFilter(configFilter) + , mDefaultFilter(new AndResourceFilter()) { + // Add the default split, which is present for all APKs. + mDefaultFilter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true)); +} + +status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs(); + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (splitConfigs.count(*iter) > 0) { + // Can't have overlapping configurations. + fprintf(stderr, "ERROR: Split configuration '%s' is already defined " + "in another split.\n", iter->toString().string()); + return ALREADY_EXISTS; + } + } + } + + sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs); + + // Add the inverse filter of this split filter to the base apk filter so it will + // omit resources that belong in this split. + mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter)); + + // Now add the apk-wide config filter to our split filter. + sp<AndResourceFilter> filter = new AndResourceFilter(); + filter->addFilter(splitFilter); + filter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(configs, filter)); + return NO_ERROR; +} + +status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + if (mSplits[i]->matches(file)) { + return mSplits.editItemAt(i)->addEntry(path, file); + } + } + // Entry can be dropped if it doesn't match any split. This will only happen + // if the enry doesn't mConfigFilter. + return NO_ERROR; +} + +void ApkBuilder::print() const { + fprintf(stderr, "APK Builder\n"); + fprintf(stderr, "-----------\n"); + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + mSplits[i]->print(); + fprintf(stderr, "\n"); + } +} + +ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase) + : mConfigs(configs), mFilter(filter), mIsBase(isBase) { + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (mName.size() > 0) { + mName.append(","); + mDirName.append("_"); + } + + String8 configStr = iter->toString(); + mName.append(configStr); + mDirName.append(configStr); + } +} + +status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) { + if (!mFiles.insert(OutputEntry(path, file)).second) { + // Duplicate file. + return ALREADY_EXISTS; + } + return NO_ERROR; +} + +void ApkSplit::print() const { + fprintf(stderr, "APK Split '%s'\n", mName.string()); + + std::set<OutputEntry>::const_iterator iter = mFiles.begin(); + for (; iter != mFiles.end(); iter++) { + fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string()); + } +} diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h new file mode 100644 index 0000000..a4b7d4a --- /dev/null +++ b/tools/aapt/ApkBuilder.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef __APK_BUILDER_H +#define __APK_BUILDER_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include "ConfigDescription.h" +#include "OutputSet.h" +#include "ResourceFilter.h" + +class ApkSplit; +class AaptFile; + +class ApkBuilder : public android::RefBase { +public: + ApkBuilder(const sp<WeakResourceFilter>& configFilter); + + /** + * Tells the builder to generate a separate APK for resources that + * match the configurations specified. Split APKs can not have + * overlapping resources. + * + * NOTE: All splits should be set up before any files are added. + */ + android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs); + + /** + * Adds a file to be written to the final APK. It's name must not collide + * with that of any files previously added. When a Split APK is being + * generated, duplicates can exist as long as they are in different splits + * (resources.arsc, AndroidManifest.xml). + */ + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + android::Vector<sp<ApkSplit> >& getSplits() { + return mSplits; + } + + void print() const; + +private: + android::sp<ResourceFilter> mConfigFilter; + android::sp<AndResourceFilter> mDefaultFilter; + android::Vector<sp<ApkSplit> > mSplits; +}; + +class ApkSplit : public OutputSet { +public: + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + const std::set<OutputEntry>& getEntries() const { + return mFiles; + } + + const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + + bool matches(const sp<AaptFile>& file) const { + return mFilter->match(file->getGroupEntry().toParams()); + } + + sp<ResourceFilter> getResourceFilter() const { + return mFilter; + } + + const android::String8& getPrintableName() const { + return mName; + } + + const android::String8& getDirectorySafeName() const { + return mDirName; + } + + bool isBase() const { + return mIsBase; + } + + void print() const; + +private: + friend class ApkBuilder; + + ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false); + + std::set<ConfigDescription> mConfigs; + const sp<ResourceFilter> mFilter; + const bool mIsBase; + String8 mName; + String8 mDirName; + std::set<OutputEntry> mFiles; +}; + +#endif // __APK_BUILDER_H diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index ebe1bed..ceb52a0 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -151,10 +151,12 @@ public: void setPublicOutputFile(const char* file) { mPublicOutputFile = file; } const char* getRClassDir() const { return mRClassDir; } void setRClassDir(const char* dir) { mRClassDir = dir; } - const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; } + const android::String8& getConfigurations() const { return mConfigurations; } void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } } - const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; } - void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } } + const android::String8& getPreferredDensity() const { return mPreferredDensity; } + void setPreferredDensity(const char* val) { mPreferredDensity = val; } + void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); } + const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; } const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; } void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; } const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; } @@ -286,7 +288,8 @@ private: const char* mRClassDir; const char* mResourceIntermediatesDir; android::String8 mConfigurations; - android::String8 mPreferredConfigurations; + android::String8 mPreferredDensity; + android::Vector<android::String8> mPartialConfigurations; android::Vector<const char*> mPackageIncludes; android::Vector<const char*> mJarFiles; android::Vector<const char*> mNoCompressExtensions; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 0af1ce1..0360200 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -3,6 +3,7 @@ // // Android Asset Packaging Tool main entry point. // +#include "ApkBuilder.h" #include "Main.h" #include "Bundle.h" #include "ResourceFilter.h" @@ -2034,6 +2035,47 @@ bail: return (result != NO_ERROR); } +static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) { + const size_t numDirs = dir->getDirs().size(); + for (size_t i = 0; i < numDirs; i++) { + status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder); + if (err != NO_ERROR) { + return err; + } + } + + const size_t numFiles = dir->getFiles().size(); + for (size_t i = 0; i < numFiles; i++) { + sp<AaptGroup> gp = dir->getFiles().valueAt(i); + const size_t numConfigs = gp->getFiles().size(); + for (size_t j = 0; j < numConfigs; j++) { + status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to add %s (%s) to builder.\n", + gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); + return err; + } + } + } + return NO_ERROR; +} + +static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { + if (split->isBase()) { + return original; + } + + String8 ext(original.getPathExtension()); + if (ext == String8(".apk")) { + return String8::format("%s_%s%s", + original.getBasePath().string(), + split->getDirectorySafeName().string(), + ext.string()); + } + + return String8::format("%s_%s", original.string(), + split->getDirectorySafeName().string()); +} /* * Package up an asset directory and associated application files. @@ -2047,17 +2089,18 @@ int doPackage(Bundle* bundle) int N; FILE* fp; String8 dependencyFile; + sp<ApkBuilder> builder; // -c en_XA or/and ar_XB means do pseudolocalization - ResourceFilter filter; - err = filter.parse(bundle->getConfigurations()); + sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); + err = configFilter->parse(bundle->getConfigurations()); if (err != NO_ERROR) { goto bail; } - if (filter.containsPseudo()) { + if (configFilter->containsPseudo()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); } - if (filter.containsPseudoBidi()) { + if (configFilter->containsPseudoBidi()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); } @@ -2105,9 +2148,32 @@ int doPackage(Bundle* bundle) assets->print(String8()); } + // Create the ApkBuilder, which will collect the compiled files + // to write to the final APK (or sets of APKs if we are building + // a Split APK. + builder = new ApkBuilder(configFilter); + + // If we are generating a Split APK, find out which configurations to split on. + if (bundle->getSplitConfigurations().size() > 0) { + const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); + const size_t numSplits = splitStrs.size(); + for (size_t i = 0; i < numSplits; i++) { + std::set<ConfigDescription> configs; + if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { + fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); + goto bail; + } + + err = builder->createSplitForConfigs(configs); + if (err != NO_ERROR) { + goto bail; + } + } + } + // If they asked for any fileAs that need to be compiled, do so. if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { - err = buildResources(bundle, assets); + err = buildResources(bundle, assets, builder); if (err != 0) { goto bail; } @@ -2194,11 +2260,24 @@ int doPackage(Bundle* bundle) // Write the apk if (outputAPKFile) { - err = writeAPK(bundle, assets, String8(outputAPKFile)); + // Gather all resources and add them to the APK Builder. The builder will then + // figure out which Split they belong in. + err = addResourcesToBuilder(assets, builder); if (err != NO_ERROR) { - fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); goto bail; } + + const Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + const sp<ApkSplit>& split = splits[i]; + String8 outputPath = buildApkName(String8(outputAPKFile), split); + err = writeAPK(bundle, outputPath, split); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); + goto bail; + } + } } // If we've been asked to generate a dependency file, we need to finish up here. diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h new file mode 100644 index 0000000..779c423 --- /dev/null +++ b/tools/aapt/ConfigDescription.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __CONFIG_DESCRIPTION_H +#define __CONFIG_DESCRIPTION_H + +#include <androidfw/ResourceTypes.h> + +/** + * Subclass of ResTable_config that adds convenient + * initialization and comparison methods. + */ +struct ConfigDescription : public android::ResTable_config { + ConfigDescription() { + memset(this, 0, sizeof(*this)); + size = sizeof(android::ResTable_config); + } + ConfigDescription(const android::ResTable_config&o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + } + ConfigDescription(const ConfigDescription&o) { + *static_cast<android::ResTable_config*>(this) = o; + } + + ConfigDescription& operator=(const android::ResTable_config& o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + return *this; + } + ConfigDescription& operator=(const ConfigDescription& o) { + *static_cast<android::ResTable_config*>(this) = o; + return *this; + } + + inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } + inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } + inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } + inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } + inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } + inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } +}; + +#endif // __CONFIG_DESCRIPTION_H diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 1cf4783..5a60014 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -70,6 +70,7 @@ void usage(void) " [-F apk-file] [-J R-file-dir] \\\n" " [--product product1,product2,...] \\\n" " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n" + " [--split CONFIGS [--split CONFIGS]] \\\n" " [raw-files-dir [raw-files-dir] ...] \\\n" " [--output-text-symbols DIR]\n" "\n" @@ -166,10 +167,12 @@ void usage(void) " generate dependency files in the same directories for R.java and resource package\n" " --auto-add-overlay\n" " Automatically add resources that are only in overlays.\n" - " --preferred-configurations\n" - " Like the -c option for filtering out unneeded configurations, but\n" - " only expresses a preference. If there is no resource available with\n" - " the preferred configuration then it will not be stripped.\n" + " --preferred-density\n" + " Specifies a preference for a particular density. Resources that do not\n" + " match this density and have variants that are a closer match are removed.\n" + " --split\n" + " Builds a separate split APK for the configurations listed. This can\n" + " be loaded alongside the base APK at runtime.\n" " --rename-manifest-package\n" " Rewrite the manifest so that its package name is the package name\n" " given here. Relative class names (for example .Foo) will be\n" @@ -568,15 +571,24 @@ int main(int argc, char* const argv[]) bundle.setGenDependencies(true); } else if (strcmp(cp, "-utf16") == 0) { bundle.setWantUTF16(true); - } else if (strcmp(cp, "-preferred-configurations") == 0) { + } else if (strcmp(cp, "-preferred-density") == 0) { argc--; argv++; if (!argc) { - fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n"); + fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n"); wantUsage = true; goto bail; } - bundle.addPreferredConfigurations(argv[0]); + bundle.setPreferredDensity(argv[0]); + } else if (strcmp(cp, "-split") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--split' option\n"); + wantUsage = true; + goto bail; + } + bundle.addSplitConfigurations(argv[0]); } else if (strcmp(cp, "-rename-manifest-package") == 0) { argc--; argv++; diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index a6b39ac..34c4496 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -10,8 +10,12 @@ #include <utils/threads.h> #include <utils/List.h> #include <utils/Errors.h> -#include "Bundle.h" +#include <utils/StrongPointer.h> + #include "AaptAssets.h" +#include "ApkBuilder.h" +#include "Bundle.h" +#include "ResourceFilter.h" #include "ZipFile.h" @@ -22,6 +26,8 @@ #include <time.h> #endif /* BENCHMARK */ +class OutputSet; + extern int doVersion(Bundle* bundle); extern int doList(Bundle* bundle); extern int doDump(Bundle* bundle); @@ -34,13 +40,13 @@ extern int doSingleCrunch(Bundle* bundle); extern int calcPercent(long uncompressedLen, long compressedLen); extern android::status_t writeAPK(Bundle* bundle, - const sp<AaptAssets>& assets, - const android::String8& outputFile); + const android::String8& outputFile, + const android::sp<OutputSet>& outputSet); extern android::status_t updatePreProcessedCache(Bundle* bundle); extern android::status_t buildResources(Bundle* bundle, - const sp<AaptAssets>& assets); + const sp<AaptAssets>& assets, sp<ApkBuilder>& builder); extern android::status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate); @@ -49,8 +55,6 @@ extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& extern bool isValidResourceType(const String8& type); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); - extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets); int dumpResources(Bundle* bundle); diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h new file mode 100644 index 0000000..ea9ef70 --- /dev/null +++ b/tools/aapt/OutputSet.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef __OUTPUT_SET_H +#define __OUTPUT_SET_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +class AaptFile; + +class OutputEntry { +public: + OutputEntry() {} + OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file) + : mPath(path), mFile(file) {} + + inline const android::sp<const AaptFile>& getFile() const { + return mFile; + } + + inline const android::String8& getPath() const { + return mPath; + } + + bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; } + bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; } + +private: + android::String8 mPath; + android::sp<const AaptFile> mFile; +}; + +class OutputSet : public virtual android::RefBase { +public: + virtual const std::set<OutputEntry>& getEntries() const = 0; + + virtual ~OutputSet() {} +}; + +#endif // __OUTPUT_SET_H diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 872d95c..dc16e35 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -5,6 +5,7 @@ // #include "Main.h" #include "AaptAssets.h" +#include "OutputSet.h" #include "ResourceTable.h" #include "ResourceFilter.h" @@ -36,11 +37,8 @@ static const char* kNoCompressExt[] = { }; /* fwd decls, so I can write this downward */ -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, - const AaptGroupEntry& ge, const ResourceFilter* filter); -bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file); +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet); +bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file); bool okayToCompress(Bundle* bundle, const String8& pathName); ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); @@ -51,8 +49,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ -status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, - const String8& outputFile) +status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet) { #if BENCHMARK fprintf(stdout, "BENCHMARK: Starting APK Bundling \n"); @@ -112,7 +109,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, printf("Writing all files...\n"); } - count = processAssets(bundle, zip, assets); + count = processAssets(bundle, zip, outputSet); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFile.string()); @@ -218,72 +215,24 @@ bail: return result; } -ssize_t processAssets(Bundle* bundle, ZipFile* zip, - const sp<AaptAssets>& assets) -{ - ResourceFilter filter; - status_t status = filter.parse(bundle->getConfigurations()); - if (status != NO_ERROR) { - return -1; - } - - ssize_t count = 0; - - const size_t N = assets->getGroupEntries().size(); - for (size_t i=0; i<N; i++) { - const AaptGroupEntry& ge = assets->getGroupEntries()[i]; - - ssize_t res = processAssets(bundle, zip, assets, ge, &filter); - if (res < 0) { - return res; - } - - count += res; - } - - return count; -} - -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, - const AaptGroupEntry& ge, const ResourceFilter* filter) +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet) { ssize_t count = 0; - - const size_t ND = dir->getDirs().size(); - size_t i; - for (i=0; i<ND; i++) { - const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); - - const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0; - - if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) { - continue; - } - - ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL); - if (res < 0) { - return res; - } - count += res; - } - - if (filter != NULL && !filter->match(ge.toParams())) { - return count; - } - - const size_t NF = dir->getFiles().size(); - for (i=0; i<NF; i++) { - sp<AaptGroup> gp = dir->getFiles().valueAt(i); - ssize_t fi = gp->getFiles().indexOfKey(ge); - if (fi >= 0) { - sp<AaptFile> fl = gp->getFiles().valueAt(fi); - if (!processFile(bundle, zip, gp, fl)) { + const std::set<OutputEntry>& entries = outputSet->getEntries(); + std::set<OutputEntry>::const_iterator iter = entries.begin(); + for (; iter != entries.end(); iter++) { + const OutputEntry& entry = *iter; + if (entry.getFile() == NULL) { + fprintf(stderr, "warning: null file being processed.\n"); + } else { + String8 storagePath(entry.getPath()); + storagePath.convertToResPath(); + if (!processFile(bundle, zip, storagePath, entry.getFile())) { return UNKNOWN_ERROR; } count++; } } - return count; } @@ -294,12 +243,10 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, * delete the existing entry before adding the new one. */ bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file) + String8 storageName, const sp<const AaptFile>& file) { const bool hasData = file->hasData(); - String8 storageName(group->getPath()); - storageName.convertToResPath(); ZipEntry* entry; bool fromGzip = false; status_t result; @@ -403,8 +350,8 @@ bool processFile(Bundle* bundle, ZipFile* zip, fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n", file->getPrintableSource().string()); } else { - fprintf(stderr, " Unable to add '%s': Zip add failed\n", - file->getPrintableSource().string()); + fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n", + file->getPrintableSource().string(), result); } return false; } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 1348be3..e599643 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -179,24 +179,6 @@ bool isValidResourceType(const String8& type) || type == "color" || type == "menu" || type == "mipmap"; } -static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) -{ - sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc")); - sp<AaptFile> file; - if (group != NULL) { - file = group->getFiles().valueFor(AaptGroupEntry()); - if (file != NULL) { - return file; - } - } - - if (!makeIfNecessary) { - return NULL; - } - return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(), - NULL, String8()); -} - static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptGroup>& grp) { @@ -359,23 +341,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; } -status_t postProcessImages(const sp<AaptAssets>& assets, - ResourceTable* table, - const sp<ResourceTypeSet>& set) -{ - ResourceDirIterator it(set, String8("drawable")); - bool hasErrors = false; - ssize_t res; - while ((res=it.next()) == NO_ERROR) { - res = postProcessImage(assets, table, it.getFile()); - if (res < NO_ERROR) { - hasErrors = true; - } - } - - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; -} - static void collect_files(const sp<AaptDir>& dir, KeyedVector<String8, sp<ResourceTypeSet> >* resources) { @@ -906,7 +871,38 @@ status_t updatePreProcessedCache(Bundle* bundle) return 0; } -status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) +status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split, + sp<AaptFile>& outFile) { + const String8 filename("AndroidManifest.xml"); + const String16 androidPrefix("android"); + const String16 androidNSUri("http://schemas.android.com/apk/res/android"); + sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); + + // Build the <manifest> tag + sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); + + // Add the 'package' attribute which is set to the original package name. + manifest->addAttribute(String16(), String16("package"), package); + + // Add the 'split' attribute which describes the configurations included. + String8 splitName("config_"); + splitName.append(split->getDirectorySafeName()); + manifest->addAttribute(String16(), String16("split"), String16(splitName)); + + // Build an empty <application> tag (required). + sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); + manifest->addChild(app); + root->addChild(manifest); + + status_t err = root->flatten(outFile, true, true); + if (err != NO_ERROR) { + return err; + } + outFile->setCompressionMethod(ZipEntry::kCompressDeflated); + return NO_ERROR; +} + +status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) { // First, look for a package file to parse. This is required to // be able to generate the resource information. @@ -1122,12 +1118,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------------- if (table.hasResources()) { - sp<AaptFile> resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - err = table.assignResourceIds(); if (err < NO_ERROR) { return err; @@ -1235,16 +1225,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } if (drawables != NULL) { - err = postProcessImages(assets, &table, drawables); - if (err != NO_ERROR) { + ResourceDirIterator it(drawables, String8("drawable")); + while ((err=it.next()) == NO_ERROR) { + err = postProcessImage(assets, &table, it.getFile()); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { hasErrors = true; } + err = NO_ERROR; } if (colors != NULL) { ResourceDirIterator it(colors, String8("color")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1320,15 +1318,35 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } - resFile = getResourceFile(assets); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } + Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + sp<ApkSplit>& split = splits.editItemAt(i); + sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), + AaptGroupEntry(), String8()); + err = table.flatten(bundle, split->getResourceFilter(), flattenedTable); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate resource table for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("resources.arsc"), flattenedTable); - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; + if (split->isBase()) { + resFile = flattenedTable; + finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + } else { + sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), + AaptGroupEntry(), String8()); + err = generateAndroidManifestForSplit(String16(assets->getPackage()), split, + generatedManifest); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("AndroidManifest.xml"), generatedManifest); + } } if (bundle->getPublicOutputFile()) { @@ -1344,18 +1362,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) table.writePublicDefinitions(String16(assets->getPackage()), fp); fclose(fp); } - - // Read resources back in, - finalResTable.add(resFile->getData(), resFile->getSize()); - -#if 0 - NOISY( - printf("Generated resources:\n"); - finalResTable.print(); - ) -#endif + + if (finalResTable.getTableCount() == 0 || resFile == NULL) { + fprintf(stderr, "No resource table was generated.\n"); + return UNKNOWN_ERROR; + } } - + // Perform a basic validation of the manifest file. This time we // parse it with the comments intact, so that we can use them to // generate java docs... so we are not going to write this one diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8ca852e..de8b4fc 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -1,119 +1,92 @@ // -// Copyright 2011 The Android Open Source Project +// Copyright 2014 The Android Open Source Project // // Build resource files from raw assets. // #include "ResourceFilter.h" +#include "AaptUtil.h" +#include "AaptConfig.h" status_t -ResourceFilter::parse(const char* arg) +WeakResourceFilter::parse(const String8& str) { - if (arg == NULL) { - return 0; - } - - const char* p = arg; - const char* q; - - while (true) { - q = strchr(p, ','); - if (q == NULL) { - q = p + strlen(p); - } - - String8 part(p, q-p); - + Vector<String8> configStrs = AaptUtil::split(str, ','); + const size_t N = configStrs.size(); + mConfigs.clear(); + mConfigMask = 0; + mConfigs.resize(N); + for (size_t i = 0; i < N; i++) { + const String8& part = configStrs[i]; if (part == "en_XA") { mContainsPseudoAccented = true; } else if (part == "ar_XB") { mContainsPseudoBidi = true; } - int axis; - AxisValue value; - if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) { - fprintf(stderr, "Invalid configuration: %s\n", arg); - fprintf(stderr, " "); - for (int i=0; i<p-arg; i++) { - fprintf(stderr, " "); - } - for (int i=0; i<q-p; i++) { - fprintf(stderr, "^"); - } - fprintf(stderr, "\n"); - return 1; - } - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - mData.add(axis, SortedVector<AxisValue>()); - } - SortedVector<AxisValue>& sv = mData.editValueFor(axis); - sv.add(value); + std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i); - // If it's a locale with a region, script or variant, we should also match an - // unmodified locale of the same language - if (axis == AXIS_LOCALE) { - if (value.localeValue.region[0] || value.localeValue.script[0] || - value.localeValue.variant[0]) { - AxisValue copy; - memcpy(copy.localeValue.language, value.localeValue.language, - sizeof(value.localeValue.language)); - sv.add(copy); - } + AaptLocaleValue val; + if (val.initFromFilterString(part)) { + // For backwards compatibility, we accept configurations that + // only specify locale in the standard 'en_US' format. + val.writeTo(&entry.first); + } else if (!AaptConfig::parse(part, &entry.first)) { + fprintf(stderr, "Invalid configuration: %s\n", part.string()); + return UNKNOWN_ERROR; } - p = q; - if (!*p) break; - p++; + + entry.second = mDefault.diff(entry.first); + + // Ignore the version + entry.second &= ~ResTable_config::CONFIG_VERSION; + + mConfigMask |= entry.second; } return NO_ERROR; } bool -ResourceFilter::isEmpty() const -{ - return mData.size() == 0; -} - -bool -ResourceFilter::match(int axis, const AxisValue& value) const +WeakResourceFilter::match(const ResTable_config& config) const { - if (value.intValue == 0 && (value.localeValue.language[0] == 0)) { - // they didn't specify anything so take everything - return true; - } - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - // we didn't request anything on this axis so take everything + uint32_t mask = mDefault.diff(config); + if ((mConfigMask & mask) == 0) { + // The two configurations don't have any common axis. return true; } - const SortedVector<AxisValue>& sv = mData.valueAt(index); - return sv.indexOf(value) >= 0; -} - -bool -ResourceFilter::match(int axis, const ResTable_config& config) const -{ - return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis)); -} -bool -ResourceFilter::match(const ResTable_config& config) const -{ - for (int i=AXIS_START; i<=AXIS_END; i++) { - if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) { - return false; + const size_t N = mConfigs.size(); + for (size_t i = 0; i < N; i++) { + const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i]; + uint32_t diff = entry.first.diff(config); + if ((diff & entry.second) == 0) { + return true; + } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) { + // If the locales differ, but the languages are the same and + // the locale we are matching only has a language specified, + // we match. + if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) { + if (config.country[0] == 0) { + return true; + } + } } } - return true; + return false; } -const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const -{ - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - return NULL; +status_t +StrongResourceFilter::parse(const String8& str) { + Vector<String8> configStrs = AaptUtil::split(str, ','); + ConfigDescription config; + mConfigs.clear(); + for (size_t i = 0; i < configStrs.size(); i++) { + if (!AaptConfig::parse(configStrs[i], &config)) { + fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string()); + return UNKNOWN_ERROR; + } + mConfigs.insert(config); } - return &mData.valueAt(index); + return NO_ERROR; } diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h index c57770e..f459584 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -7,31 +7,137 @@ #ifndef RESOURCE_FILTER_H #define RESOURCE_FILTER_H +#include <androidfw/ResourceTypes.h> +#include <set> +#include <utility> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + #include "AaptAssets.h" +#include "ConfigDescription.h" + +class ResourceFilter : public virtual android::RefBase { +public: + virtual bool match(const android::ResTable_config& config) const = 0; +}; /** * Implements logic for parsing and handling "-c" and "--preferred-configurations" * options. */ -class ResourceFilter -{ +class WeakResourceFilter : public ResourceFilter { public: - ResourceFilter() : mData(), mContainsPseudoAccented(false), - mContainsPseudoBidi(false) {} - status_t parse(const char* arg); - bool isEmpty() const; - bool match(int axis, const ResTable_config& config) const; - bool match(const ResTable_config& config) const; - const SortedVector<AxisValue>* configsForAxis(int axis) const; - inline bool containsPseudo() const { return mContainsPseudoAccented; } - inline bool containsPseudoBidi() const { return mContainsPseudoBidi; } + WeakResourceFilter() + : mContainsPseudoAccented(false) + , mContainsPseudoBidi(false) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const; + + inline bool isEmpty() const { + return mConfigMask == 0; + } + + inline bool containsPseudo() const { + return mContainsPseudoAccented; + } + + inline bool containsPseudoBidi() const { + return mContainsPseudoBidi; + } private: - bool match(int axis, const AxisValue& value) const; + ConfigDescription mDefault; + uint32_t mConfigMask; + android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs; - KeyedVector<int,SortedVector<AxisValue> > mData; bool mContainsPseudoAccented; bool mContainsPseudoBidi; }; +/** + * Matches resources that have at least one of the configurations + * that this filter is looking for. In order to match a configuration, + * the resource must have the exact same configuration. + * + * This filter acts as a logical OR when matching resources. + * + * For example, if the filter is looking for resources with + * fr-land, de-land, or sw600dp: + * + * (PASS) fr-land + * (FAIL) fr + * (PASS) de-land + * (FAIL) de + * (FAIL) de-sw600dp + * (PASS) sw600dp + * (FAIL) sw600dp-land + */ +class StrongResourceFilter : public ResourceFilter { +public: + StrongResourceFilter() {} + StrongResourceFilter(const std::set<ConfigDescription>& configs) + : mConfigs(configs) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const { + std::set<ConfigDescription>::const_iterator iter = mConfigs.begin(); + for (; iter != mConfigs.end(); iter++) { + if (iter->compare(config) == 0) { + return true; + } + } + return false; + } + + inline const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + +private: + std::set<ConfigDescription> mConfigs; +}; + +/** + * Negates the response of the target filter. + */ +class InverseResourceFilter : public ResourceFilter { +public: + InverseResourceFilter(const android::sp<ResourceFilter>& filter) + : mFilter(filter) {} + + bool match(const android::ResTable_config& config) const { + return !mFilter->match(config); + } + +private: + const android::sp<ResourceFilter> mFilter; +}; + +/** + * A logical AND of all the added filters. + */ +class AndResourceFilter : public ResourceFilter { +public: + void addFilter(const android::sp<ResourceFilter>& filter) { + mFilters.add(filter); + } + + bool match(const android::ResTable_config& config) const { + const size_t N = mFilters.size(); + for (size_t i = 0; i < N; i++) { + if (!mFilters[i]->match(config)) { + return false; + } + } + return true; + } + +private: + android::Vector<android::sp<ResourceFilter> > mFilters; +}; #endif diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 6eab65b..efbba40 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2095,10 +2095,10 @@ bool ResourceTable::hasResources() const { return mNumLocal > 0; } -sp<AaptFile> ResourceTable::flatten(Bundle* bundle) +sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter) { sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8()); - status_t err = flatten(bundle, data); + status_t err = flatten(bundle, filter, data); return err == NO_ERROR ? data : NULL; } @@ -2658,8 +2658,8 @@ ResourceTable::validateLocalizations(void) } // Check that all requested localizations are present for this string - if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) { - const char* allConfigs = mBundle->getConfigurations(); + if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) { + const char* allConfigs = mBundle->getConfigurations().string(); const char* start = allConfigs; const char* comma; @@ -2713,14 +2713,8 @@ ResourceTable::validateLocalizations(void) return err; } -status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) +status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest) { - ResourceFilter filter; - status_t err = filter.parse(bundle->getConfigurations()); - if (err != NO_ERROR) { - return err; - } - const ConfigDescription nullConfig; const size_t N = mOrderedPackages.size(); @@ -2795,7 +2789,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) const size_t N = c->getEntries().size(); for (size_t ei=0; ei<N; ei++) { ConfigDescription config = c->getEntries().keyAt(ei); - if (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } sp<Entry> e = c->getEntries().valueAt(ei); @@ -2887,7 +2881,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return amt; } - err = flattenLibraryTable(data, libraryPackages); + status_t err = flattenLibraryTable(data, libraryPackages); if (err != NO_ERROR) { fprintf(stderr, "ERROR: failed to write library table\n"); return err; @@ -2943,11 +2937,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } const size_t CN = cl->getEntries().size(); for (size_t ci=0; ci<CN; ci++) { - if (filterable && !filter.match(cl->getEntries().keyAt(ci))) { + if (filterable && !filter->match(cl->getEntries().keyAt(ci))) { continue; } for (size_t cj=ci+1; cj<CN; cj++) { - if (filterable && !filter.match(cl->getEntries().keyAt(cj))) { + if (filterable && !filter->match(cl->getEntries().keyAt(cj))) { continue; } typeSpecFlags[ei] |= htodl( @@ -2989,7 +2983,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) config.screenHeightDp, config.layoutDirection)); - if (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } @@ -3108,7 +3102,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } ssize_t strStart = dest->getSize(); - err = valueStrings.writeStringBlock(dest); + status_t err = valueStrings.writeStringBlock(dest); if (err != NO_ERROR) { return err; } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index ec8fd17..a73993c 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -7,8 +7,10 @@ #ifndef RESOURCE_TABLE_H #define RESOURCE_TABLE_H +#include "ConfigDescription.h" #include "StringPool.h" #include "SourcePos.h" +#include "ResourceFilter.h" #include <set> #include <map> @@ -76,37 +78,6 @@ public: class Type; class Entry; - struct ConfigDescription : public ResTable_config { - ConfigDescription() { - memset(this, 0, sizeof(*this)); - size = sizeof(ResTable_config); - } - ConfigDescription(const ResTable_config&o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - } - ConfigDescription(const ConfigDescription&o) { - *static_cast<ResTable_config*>(this) = o; - } - - ConfigDescription& operator=(const ResTable_config& o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - return *this; - } - ConfigDescription& operator=(const ConfigDescription& o) { - *static_cast<ResTable_config*>(this) = o; - return *this; - } - - inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } - inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } - inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } - inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } - inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } - inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } - }; - ResourceTable(Bundle* bundle, const String16& assetsPackage); status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets); @@ -182,7 +153,7 @@ public: size_t numLocalResources() const; bool hasResources() const; - sp<AaptFile> flatten(Bundle*); + sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter); static inline uint32_t makeResId(uint32_t packageId, uint32_t typeId, @@ -223,7 +194,7 @@ public: void addLocalization(const String16& name, const String8& locale, const SourcePos& src); status_t validateLocalizations(void); - status_t flatten(Bundle*, const sp<AaptFile>& dest); + status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest); status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs); void writePublicDefinitions(const String16& package, FILE* fp); diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp new file mode 100644 index 0000000..e795d81 --- /dev/null +++ b/tools/aapt/tests/AaptConfig_test.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ConfigDescription.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) { + if (AaptConfig::parse(String8(input), config)) { + return ::testing::AssertionSuccess() << input << " was successfully parsed"; + } + return ::testing::AssertionFailure() << input << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) { + return TestParse(String8(input), config); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) { + EXPECT_FALSE(TestParse("en-sw600dp-ldrtl")); + EXPECT_FALSE(TestParse("land-en")); + EXPECT_FALSE(TestParse("hdpi-320dpi")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) { + EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) { + EXPECT_FALSE(TestParse("en-sw600dp-land-")); +} + +TEST(AaptConfigTest, ParseBasicQualifiers) { + ConfigDescription config; + EXPECT_TRUE(TestParse("", &config)); + EXPECT_EQ(String8(""), config.toString()); + + EXPECT_TRUE(TestParse("fr-land", &config)); + EXPECT_EQ(String8("fr-land"), config.toString()); + + EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav", &config)); + EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString()); +} + +TEST(AaptConfigTest, ParseLocales) { + ConfigDescription config; + EXPECT_TRUE(TestParse("en-rUS", &config)); + EXPECT_EQ(String8("en-US"), config.toString()); +} + +TEST(AaptConfigTest, ParseQualifierAddedInApi13) { + ConfigDescription config; + EXPECT_TRUE(TestParse("sw600dp", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); + + EXPECT_TRUE(TestParse("sw600dp-v8", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); +} diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp new file mode 100644 index 0000000..7348a08 --- /dev/null +++ b/tools/aapt/tests/AaptGroupEntry_test.cpp @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptAssets.h" +#include "ResourceFilter.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName, + String8* outType) { + if (entry.initFromDirName(dirName, outType)) { + return ::testing::AssertionSuccess() << dirName << " was successfully parsed"; + } + return ::testing::AssertionFailure() << dirName << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input, + String8* outType) { + return TestParse(entry, String8(input), outType); +} + +TEST(AaptGroupEntryTest, ParseNoQualifier) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "menu", &type)); + EXPECT_EQ(String8("menu"), type); +} + +TEST(AaptGroupEntryTest, ParseCorrectType) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "anim", &type)); + EXPECT_EQ(String8("anim"), type); + + EXPECT_TRUE(TestParse(entry, "animator", &type)); + EXPECT_EQ(String8("animator"), type); +} diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp new file mode 100644 index 0000000..30697bb --- /dev/null +++ b/tools/aapt/tests/ResourceFilter_test.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ResourceFilter.h" +#include "ConfigDescription.h" + +using android::String8; + +// In this context, 'Axis' represents a particular field in the configuration, +// such as language or density. + +TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8(""))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); + + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_FALSE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) { + WeakResourceFilter filter; + EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE"))); +} + +TEST(WeakResourceFilterTest, IgnoresVersion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4"))); + + ConfigDescription config; + config.smallestScreenWidthDp = 600; + config.version = 13; + + // The configs don't match on any axis besides version, which should be ignored. + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithRegion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419"))); + + ConfigDescription config; + AaptLocaleValue val; + ASSERT_TRUE(val.initFromFilterString(String8("kok_IN"))); + val.writeTo(&config); + + EXPECT_TRUE(filter.match(config)); +} + diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h new file mode 100644 index 0000000..7917483 --- /dev/null +++ b/tools/aapt/tests/TestHelper.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef __TEST_HELPER_H +#define __TEST_HELPER_H + +#include <utils/String8.h> + +namespace android { + +/** + * Stream operator for nicely printing String8's in gtest output. + */ +inline std::ostream& operator<<(std::ostream& stream, const String8& str) { + return stream << str.string(); +} + +} + +#endif diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index ce8c8b8..85b81d9 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -506,6 +506,12 @@ public class WifiConfiguration implements Parcelable { * @hide */ public boolean isValid() { + if (SSID == null) + return false; + + if (allowedKeyManagement == null) + return false; + if (allowedKeyManagement.cardinality() > 1) { if (allowedKeyManagement.cardinality() != 2) { return false; |
