diff options
81 files changed, 2700 insertions, 1153 deletions
diff --git a/api/current.txt b/api/current.txt index 9cd3ee35fae5..243c78f80bad 100644 --- a/api/current.txt +++ b/api/current.txt @@ -203,6 +203,8 @@ package android { public static final class R.attr { ctor public R.attr(); + field public static final int __removed0 = 16844097; // 0x1010541 + field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -758,7 +760,6 @@ package android { field public static final int keyboardLayout = 16843691; // 0x10103ab field public static final int keyboardMode = 16843341; // 0x101024d field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 - field public static final int keyboardNavigationSection = 16844097; // 0x1010541 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c field public static final int label = 16842753; // 0x1010001 @@ -908,7 +909,6 @@ package android { field public static final int nextFocusLeft = 16842977; // 0x10100e1 field public static final int nextFocusRight = 16842978; // 0x10100e2 field public static final int nextFocusUp = 16842979; // 0x10100e3 - field public static final int nextSectionForward = 16844099; // 0x1010543 field public static final int noHistory = 16843309; // 0x101022d field public static final int normalScreens = 16843397; // 0x1010285 field public static final int notificationTimeout = 16843651; // 0x1010383 @@ -1817,6 +1817,7 @@ package android { field public static final int tabs = 16908307; // 0x1020013 field public static final int text1 = 16908308; // 0x1020014 field public static final int text2 = 16908309; // 0x1020015 + field public static final int textAssist = 16908353; // 0x1020041 field public static final int title = 16908310; // 0x1020016 field public static final int toggle = 16908311; // 0x1020017 field public static final int undo = 16908338; // 0x1020032 @@ -3536,7 +3537,6 @@ package android.app { method public int getRequestedOrientation(); method public final android.view.SearchEvent getSearchEvent(); method public int getTaskId(); - method public android.text.TextAssistant getTextAssistant(); method public final java.lang.CharSequence getTitle(); method public final int getTitleColor(); method public android.app.VoiceInteractor getVoiceInteractor(); @@ -3686,7 +3686,6 @@ package android.app { method public final void setResult(int, android.content.Intent); method public final deprecated void setSecondaryProgress(int); method public void setTaskDescription(android.app.ActivityManager.TaskDescription); - method public void setTextAssistant(android.text.TextAssistant); method public void setTitle(java.lang.CharSequence); method public void setTitle(int); method public deprecated void setTitleColor(int); @@ -5583,6 +5582,18 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public java.lang.CharSequence getUserActionTitle(); + method public java.lang.CharSequence getUserMessage(); + method public void showAsDialog(android.app.Activity); + method public void showAsNotification(android.content.Context); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; + } + public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener); method public android.app.RemoteAction clone(); @@ -9748,6 +9759,7 @@ package android.content.pm { field public boolean enabled; field public boolean exported; field public java.lang.String processName; + field public java.lang.String splitName; } public class ConfigurationInfo implements android.os.Parcelable { @@ -30454,14 +30466,22 @@ package android.os.storage { } public class StorageManager { + method public long getCacheQuotaBytes(); + method public long getCacheSizeBytes(); + method public long getExternalCacheQuotaBytes(); + method public long getExternalCacheSizeBytes(); method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; + method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException; + method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } @@ -35768,6 +35788,7 @@ package android.service.notification { public static class NotificationListenerService.Ranking { ctor public NotificationListenerService.Ranking(); + method public boolean canShowBadge(); method public java.util.List<java.lang.String> getAdditionalPeople(); method public android.app.NotificationChannel getChannel(); method public int getImportance(); @@ -35809,7 +35830,6 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); - method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -39906,22 +39926,6 @@ package android.text { method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic); } - public abstract interface TextAssistant { - method public abstract void addLinks(android.text.Spannable, int); - method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - - public class TextClassification { - ctor public TextClassification(); - method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence(); - } - - public final class TextClassificationManager implements android.text.TextAssistant { - method public void addLinks(android.text.Spannable, int); - method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - public abstract interface TextDirectionHeuristic { method public abstract boolean isRtl(char[], int, int); method public abstract boolean isRtl(java.lang.CharSequence, int, int); @@ -39937,13 +39941,6 @@ package android.text { field public static final android.text.TextDirectionHeuristic RTL; } - public final class TextLanguage { - ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>); - method public int getEndIndex(); - method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence(); - method public int getStartIndex(); - } - public class TextPaint extends android.graphics.Paint { ctor public TextPaint(); ctor public TextPaint(int); @@ -39956,13 +39953,6 @@ package android.text { field public int linkColor; } - public class TextSelection { - ctor public TextSelection(); - method public int getSelectionEndIndex(); - method public int getSelectionStartIndex(); - method public android.text.TextClassification getTextClassification(); - } - public class TextUtils { method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String); method public static java.lang.CharSequence concat(java.lang.CharSequence...); @@ -42216,7 +42206,9 @@ package android.view { method public android.view.Display.Mode[] getSupportedModes(); method public deprecated float[] getSupportedRefreshRates(); method public deprecated int getWidth(); + method public boolean isHdr(); method public boolean isValid(); + method public boolean isWideColorGamut(); field public static final int DEFAULT_DISPLAY = 0; // 0x0 field public static final int FLAG_PRESENTATION = 8; // 0x8 field public static final int FLAG_PRIVATE = 4; // 0x4 @@ -42285,7 +42277,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43583,7 +43575,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -43747,7 +43739,6 @@ package android.view { method public int getNextFocusLeftId(); method public int getNextFocusRightId(); method public int getNextFocusUpId(); - method public int getNextSectionForwardId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method public android.view.ViewOutlineProvider getOutlineProvider(); method public int getOverScrollMode(); @@ -43853,7 +43844,6 @@ package android.view { method public boolean isInLayout(); method public boolean isInTouchMode(); method public final boolean isKeyboardNavigationCluster(); - method public final boolean isKeyboardNavigationSection(); method public boolean isLaidOut(); method public boolean isLayoutDirectionResolved(); method public boolean isLayoutRequested(); @@ -43876,7 +43866,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -44026,7 +44016,6 @@ package android.view { method public void setImportantForAccessibility(int); method public void setKeepScreenOn(boolean); method public void setKeyboardNavigationCluster(boolean); - method public void setKeyboardNavigationSection(boolean); method public void setLabelFor(int); method public void setLayerPaint(android.graphics.Paint); method public void setLayerType(int, android.graphics.Paint); @@ -44044,7 +44033,6 @@ package android.view { method public void setNextFocusLeftId(int); method public void setNextFocusRightId(int); method public void setNextFocusUpId(int); - method public void setNextSectionForwardId(int); method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener); method public void setOnClickListener(android.view.View.OnClickListener); method public void setOnContextClickListener(android.view.View.OnContextClickListener); @@ -44171,8 +44159,6 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 - field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1 - field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 field public static final int LAYER_TYPE_NONE = 0; // 0x0 field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1 @@ -44691,7 +44677,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); @@ -46478,6 +46464,83 @@ package android.view.inputmethod { } +package android.view.textclassifier { + + public abstract interface LinksInfo { + method public abstract boolean apply(java.lang.CharSequence); + } + + public final class TextClassificationManager { + method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); + method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + } + + public final class TextClassificationResult { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public android.graphics.drawable.Drawable getIcon(); + method public android.content.Intent getIntent(); + method public java.lang.CharSequence getLabel(); + method public android.view.View.OnClickListener getOnClickListener(); + method public java.lang.String getText(); + } + + public static final class TextClassificationResult.Builder { + ctor public TextClassificationResult.Builder(); + method public android.view.textclassifier.TextClassificationResult build(); + method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float); + method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable); + method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent); + method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String); + method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener); + method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String); + } + + public abstract interface TextClassifier { + method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int); + method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int); + method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int); + field public static final android.view.textclassifier.TextClassifier NO_OP; + field public static final java.lang.String TYPE_ADDRESS = "address"; + field public static final java.lang.String TYPE_EMAIL = "email"; + field public static final java.lang.String TYPE_OTHER = "other"; + field public static final java.lang.String TYPE_PHONE = "phone"; + } + + public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation { + } + + public final class TextLanguage { + method public float getConfidenceScore(java.util.Locale); + method public int getEndIndex(); + method public java.util.Locale getLanguage(int); + method public int getLanguageCount(); + method public int getStartIndex(); + } + + public static final class TextLanguage.Builder { + ctor public TextLanguage.Builder(int, int); + method public android.view.textclassifier.TextLanguage build(); + method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float); + } + + public final class TextSelection { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public int getSelectionEndIndex(); + method public int getSelectionStartIndex(); + } + + public static final class TextSelection.Builder { + ctor public TextSelection.Builder(int, int); + method public android.view.textclassifier.TextSelection build(); + method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float); + } + +} + package android.view.textservice { public final class SentenceSuggestionsInfo implements android.os.Parcelable { @@ -49567,7 +49630,7 @@ package android.widget { method public float getShadowRadius(); method public final boolean getShowSoftInputOnFocus(); method public java.lang.CharSequence getText(); - method public android.text.TextAssistant getTextAssistant(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public final android.content.res.ColorStateList getTextColors(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); @@ -49683,7 +49746,7 @@ package android.widget { method public final void setText(int, android.widget.TextView.BufferType); method public void setTextAppearance(int); method public deprecated void setTextAppearance(android.content.Context, int); - method public void setTextAssistant(android.text.TextAssistant); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public void setTextColor(int); method public void setTextColor(android.content.res.ColorStateList); method public void setTextIsSelectable(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index ebe0660ef8e7..063c3c3a0e34 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -312,6 +312,8 @@ package android { public static final class R.attr { ctor public R.attr(); + field public static final int __removed0 = 16844097; // 0x1010541 + field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -867,7 +869,6 @@ package android { field public static final int keyboardLayout = 16843691; // 0x10103ab field public static final int keyboardMode = 16843341; // 0x101024d field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 - field public static final int keyboardNavigationSection = 16844097; // 0x1010541 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c field public static final int label = 16842753; // 0x1010001 @@ -1017,7 +1018,6 @@ package android { field public static final int nextFocusLeft = 16842977; // 0x10100e1 field public static final int nextFocusRight = 16842978; // 0x10100e2 field public static final int nextFocusUp = 16842979; // 0x10100e3 - field public static final int nextSectionForward = 16844099; // 0x1010543 field public static final int noHistory = 16843309; // 0x101022d field public static final int normalScreens = 16843397; // 0x1010285 field public static final int notificationTimeout = 16843651; // 0x1010383 @@ -1930,6 +1930,7 @@ package android { field public static final int tabs = 16908307; // 0x1020013 field public static final int text1 = 16908308; // 0x1020014 field public static final int text2 = 16908309; // 0x1020015 + field public static final int textAssist = 16908353; // 0x1020041 field public static final int title = 16908310; // 0x1020016 field public static final int toggle = 16908311; // 0x1020017 field public static final int undo = 16908338; // 0x1020032 @@ -3655,7 +3656,6 @@ package android.app { method public int getRequestedOrientation(); method public final android.view.SearchEvent getSearchEvent(); method public int getTaskId(); - method public android.text.TextAssistant getTextAssistant(); method public final java.lang.CharSequence getTitle(); method public final int getTitleColor(); method public android.app.VoiceInteractor getVoiceInteractor(); @@ -3807,7 +3807,6 @@ package android.app { method public final void setResult(int, android.content.Intent); method public final deprecated void setSecondaryProgress(int); method public void setTaskDescription(android.app.ActivityManager.TaskDescription); - method public void setTextAssistant(android.text.TextAssistant); method public void setTitle(java.lang.CharSequence); method public void setTitle(int); method public deprecated void setTitleColor(int); @@ -5772,6 +5771,18 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public java.lang.CharSequence getUserActionTitle(); + method public java.lang.CharSequence getUserMessage(); + method public void showAsDialog(android.app.Activity); + method public void showAsNotification(android.content.Context); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; + } + public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener); method public android.app.RemoteAction clone(); @@ -10152,6 +10163,7 @@ package android.content.pm { field public boolean enabled; field public boolean exported; field public java.lang.String processName; + field public java.lang.String splitName; } public class ConfigurationInfo implements android.os.Parcelable { @@ -33208,14 +33220,22 @@ package android.os.storage { } public class StorageManager { + method public long getCacheQuotaBytes(); + method public long getCacheSizeBytes(); + method public long getExternalCacheQuotaBytes(); + method public long getExternalCacheSizeBytes(); method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; + method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException; + method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } @@ -38713,6 +38733,7 @@ package android.service.notification { public static class NotificationListenerService.Ranking { ctor public NotificationListenerService.Ranking(); + method public boolean canShowBadge(); method public java.util.List<java.lang.String> getAdditionalPeople(); method public android.app.NotificationChannel getChannel(); method public int getImportance(); @@ -38754,7 +38775,6 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); - method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -43209,22 +43229,6 @@ package android.text { method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic); } - public abstract interface TextAssistant { - method public abstract void addLinks(android.text.Spannable, int); - method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - - public class TextClassification { - ctor public TextClassification(); - method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence(); - } - - public final class TextClassificationManager implements android.text.TextAssistant { - method public void addLinks(android.text.Spannable, int); - method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - public abstract interface TextDirectionHeuristic { method public abstract boolean isRtl(char[], int, int); method public abstract boolean isRtl(java.lang.CharSequence, int, int); @@ -43240,13 +43244,6 @@ package android.text { field public static final android.text.TextDirectionHeuristic RTL; } - public final class TextLanguage { - ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>); - method public int getEndIndex(); - method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence(); - method public int getStartIndex(); - } - public class TextPaint extends android.graphics.Paint { ctor public TextPaint(); ctor public TextPaint(int); @@ -43259,13 +43256,6 @@ package android.text { field public int linkColor; } - public class TextSelection { - ctor public TextSelection(); - method public int getSelectionEndIndex(); - method public int getSelectionStartIndex(); - method public android.text.TextClassification getTextClassification(); - } - public class TextUtils { method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String); method public static java.lang.CharSequence concat(java.lang.CharSequence...); @@ -45520,7 +45510,9 @@ package android.view { method public android.view.Display.Mode[] getSupportedModes(); method public deprecated float[] getSupportedRefreshRates(); method public deprecated int getWidth(); + method public boolean isHdr(); method public boolean isValid(); + method public boolean isWideColorGamut(); field public static final int DEFAULT_DISPLAY = 0; // 0x0 field public static final int FLAG_PRESENTATION = 8; // 0x8 field public static final int FLAG_PRIVATE = 4; // 0x4 @@ -45589,7 +45581,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -46887,7 +46879,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -47051,7 +47043,6 @@ package android.view { method public int getNextFocusLeftId(); method public int getNextFocusRightId(); method public int getNextFocusUpId(); - method public int getNextSectionForwardId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method public android.view.ViewOutlineProvider getOutlineProvider(); method public int getOverScrollMode(); @@ -47157,7 +47148,6 @@ package android.view { method public boolean isInLayout(); method public boolean isInTouchMode(); method public final boolean isKeyboardNavigationCluster(); - method public final boolean isKeyboardNavigationSection(); method public boolean isLaidOut(); method public boolean isLayoutDirectionResolved(); method public boolean isLayoutRequested(); @@ -47180,7 +47170,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -47330,7 +47320,6 @@ package android.view { method public void setImportantForAccessibility(int); method public void setKeepScreenOn(boolean); method public void setKeyboardNavigationCluster(boolean); - method public void setKeyboardNavigationSection(boolean); method public void setLabelFor(int); method public void setLayerPaint(android.graphics.Paint); method public void setLayerType(int, android.graphics.Paint); @@ -47348,7 +47337,6 @@ package android.view { method public void setNextFocusLeftId(int); method public void setNextFocusRightId(int); method public void setNextFocusUpId(int); - method public void setNextSectionForwardId(int); method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener); method public void setOnClickListener(android.view.View.OnClickListener); method public void setOnContextClickListener(android.view.View.OnContextClickListener); @@ -47475,8 +47463,6 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 - field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1 - field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 field public static final int LAYER_TYPE_NONE = 0; // 0x0 field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1 @@ -47995,7 +47981,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); @@ -49785,6 +49771,83 @@ package android.view.inputmethod { } +package android.view.textclassifier { + + public abstract interface LinksInfo { + method public abstract boolean apply(java.lang.CharSequence); + } + + public final class TextClassificationManager { + method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); + method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + } + + public final class TextClassificationResult { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public android.graphics.drawable.Drawable getIcon(); + method public android.content.Intent getIntent(); + method public java.lang.CharSequence getLabel(); + method public android.view.View.OnClickListener getOnClickListener(); + method public java.lang.String getText(); + } + + public static final class TextClassificationResult.Builder { + ctor public TextClassificationResult.Builder(); + method public android.view.textclassifier.TextClassificationResult build(); + method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float); + method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable); + method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent); + method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String); + method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener); + method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String); + } + + public abstract interface TextClassifier { + method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int); + method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int); + method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int); + field public static final android.view.textclassifier.TextClassifier NO_OP; + field public static final java.lang.String TYPE_ADDRESS = "address"; + field public static final java.lang.String TYPE_EMAIL = "email"; + field public static final java.lang.String TYPE_OTHER = "other"; + field public static final java.lang.String TYPE_PHONE = "phone"; + } + + public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation { + } + + public final class TextLanguage { + method public float getConfidenceScore(java.util.Locale); + method public int getEndIndex(); + method public java.util.Locale getLanguage(int); + method public int getLanguageCount(); + method public int getStartIndex(); + } + + public static final class TextLanguage.Builder { + ctor public TextLanguage.Builder(int, int); + method public android.view.textclassifier.TextLanguage build(); + method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float); + } + + public final class TextSelection { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public int getSelectionEndIndex(); + method public int getSelectionStartIndex(); + } + + public static final class TextSelection.Builder { + ctor public TextSelection.Builder(int, int); + method public android.view.textclassifier.TextSelection build(); + method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float); + } + +} + package android.view.textservice { public final class SentenceSuggestionsInfo implements android.os.Parcelable { @@ -53235,7 +53298,7 @@ package android.widget { method public float getShadowRadius(); method public final boolean getShowSoftInputOnFocus(); method public java.lang.CharSequence getText(); - method public android.text.TextAssistant getTextAssistant(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public final android.content.res.ColorStateList getTextColors(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); @@ -53351,7 +53414,7 @@ package android.widget { method public final void setText(int, android.widget.TextView.BufferType); method public void setTextAppearance(int); method public deprecated void setTextAppearance(android.content.Context, int); - method public void setTextAssistant(android.text.TextAssistant); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public void setTextColor(int); method public void setTextColor(android.content.res.ColorStateList); method public void setTextIsSelectable(boolean); diff --git a/api/test-current.txt b/api/test-current.txt index df82a8aa6893..4fcaf6926cae 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -203,6 +203,8 @@ package android { public static final class R.attr { ctor public R.attr(); + field public static final int __removed0 = 16844097; // 0x1010541 + field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -758,7 +760,6 @@ package android { field public static final int keyboardLayout = 16843691; // 0x10103ab field public static final int keyboardMode = 16843341; // 0x101024d field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 - field public static final int keyboardNavigationSection = 16844097; // 0x1010541 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c field public static final int label = 16842753; // 0x1010001 @@ -908,7 +909,6 @@ package android { field public static final int nextFocusLeft = 16842977; // 0x10100e1 field public static final int nextFocusRight = 16842978; // 0x10100e2 field public static final int nextFocusUp = 16842979; // 0x10100e3 - field public static final int nextSectionForward = 16844099; // 0x1010543 field public static final int noHistory = 16843309; // 0x101022d field public static final int normalScreens = 16843397; // 0x1010285 field public static final int notificationTimeout = 16843651; // 0x1010383 @@ -1817,6 +1817,7 @@ package android { field public static final int tabs = 16908307; // 0x1020013 field public static final int text1 = 16908308; // 0x1020014 field public static final int text2 = 16908309; // 0x1020015 + field public static final int textAssist = 16908353; // 0x1020041 field public static final int title = 16908310; // 0x1020016 field public static final int toggle = 16908311; // 0x1020017 field public static final int undo = 16908338; // 0x1020032 @@ -3538,7 +3539,6 @@ package android.app { method public int getRequestedOrientation(); method public final android.view.SearchEvent getSearchEvent(); method public int getTaskId(); - method public android.text.TextAssistant getTextAssistant(); method public final java.lang.CharSequence getTitle(); method public final int getTitleColor(); method public android.app.VoiceInteractor getVoiceInteractor(); @@ -3688,7 +3688,6 @@ package android.app { method public final void setResult(int, android.content.Intent); method public final deprecated void setSecondaryProgress(int); method public void setTaskDescription(android.app.ActivityManager.TaskDescription); - method public void setTextAssistant(android.text.TextAssistant); method public void setTitle(java.lang.CharSequence); method public void setTitle(int); method public deprecated void setTitleColor(int); @@ -5594,6 +5593,18 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public java.lang.CharSequence getUserActionTitle(); + method public java.lang.CharSequence getUserMessage(); + method public void showAsDialog(android.app.Activity); + method public void showAsNotification(android.content.Context); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; + } + public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener); method public android.app.RemoteAction clone(); @@ -9775,6 +9786,7 @@ package android.content.pm { field public boolean enabled; field public boolean exported; field public java.lang.String processName; + field public java.lang.String splitName; } public class ConfigurationInfo implements android.os.Parcelable { @@ -30567,14 +30579,22 @@ package android.os.storage { } public class StorageManager { + method public long getCacheQuotaBytes(); + method public long getCacheSizeBytes(); + method public long getExternalCacheQuotaBytes(); + method public long getExternalCacheSizeBytes(); method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; + method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException; + method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } @@ -35889,6 +35909,7 @@ package android.service.notification { public static class NotificationListenerService.Ranking { ctor public NotificationListenerService.Ranking(); + method public boolean canShowBadge(); method public java.util.List<java.lang.String> getAdditionalPeople(); method public android.app.NotificationChannel getChannel(); method public int getImportance(); @@ -35930,7 +35951,6 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); - method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -40030,22 +40050,6 @@ package android.text { method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic); } - public abstract interface TextAssistant { - method public abstract void addLinks(android.text.Spannable, int); - method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - - public class TextClassification { - ctor public TextClassification(); - method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence(); - } - - public final class TextClassificationManager implements android.text.TextAssistant { - method public void addLinks(android.text.Spannable, int); - method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int); - } - public abstract interface TextDirectionHeuristic { method public abstract boolean isRtl(char[], int, int); method public abstract boolean isRtl(java.lang.CharSequence, int, int); @@ -40061,13 +40065,6 @@ package android.text { field public static final android.text.TextDirectionHeuristic RTL; } - public final class TextLanguage { - ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>); - method public int getEndIndex(); - method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence(); - method public int getStartIndex(); - } - public class TextPaint extends android.graphics.Paint { ctor public TextPaint(); ctor public TextPaint(int); @@ -40080,13 +40077,6 @@ package android.text { field public int linkColor; } - public class TextSelection { - ctor public TextSelection(); - method public int getSelectionEndIndex(); - method public int getSelectionStartIndex(); - method public android.text.TextClassification getTextClassification(); - } - public class TextUtils { method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String); method public static java.lang.CharSequence concat(java.lang.CharSequence...); @@ -42505,7 +42495,9 @@ package android.view { method public android.view.Display.Mode[] getSupportedModes(); method public deprecated float[] getSupportedRefreshRates(); method public deprecated int getWidth(); + method public boolean isHdr(); method public boolean isValid(); + method public boolean isWideColorGamut(); field public static final int DEFAULT_DISPLAY = 0; // 0x0 field public static final int FLAG_PRESENTATION = 8; // 0x8 field public static final int FLAG_PRIVATE = 4; // 0x4 @@ -42574,7 +42566,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43874,7 +43866,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -44038,7 +44030,6 @@ package android.view { method public int getNextFocusLeftId(); method public int getNextFocusRightId(); method public int getNextFocusUpId(); - method public int getNextSectionForwardId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method public android.view.ViewOutlineProvider getOutlineProvider(); method public int getOverScrollMode(); @@ -44145,7 +44136,6 @@ package android.view { method public boolean isInLayout(); method public boolean isInTouchMode(); method public final boolean isKeyboardNavigationCluster(); - method public final boolean isKeyboardNavigationSection(); method public boolean isLaidOut(); method public boolean isLayoutDirectionResolved(); method public boolean isLayoutRequested(); @@ -44168,7 +44158,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -44318,7 +44308,6 @@ package android.view { method public void setImportantForAccessibility(int); method public void setKeepScreenOn(boolean); method public void setKeyboardNavigationCluster(boolean); - method public void setKeyboardNavigationSection(boolean); method public void setLabelFor(int); method public void setLayerPaint(android.graphics.Paint); method public void setLayerType(int, android.graphics.Paint); @@ -44336,7 +44325,6 @@ package android.view { method public void setNextFocusLeftId(int); method public void setNextFocusRightId(int); method public void setNextFocusUpId(int); - method public void setNextSectionForwardId(int); method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener); method public void setOnClickListener(android.view.View.OnClickListener); method public void setOnContextClickListener(android.view.View.OnContextClickListener); @@ -44463,8 +44451,6 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 - field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1 - field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 field public static final int LAYER_TYPE_NONE = 0; // 0x0 field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1 @@ -44987,7 +44973,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); @@ -46776,6 +46762,83 @@ package android.view.inputmethod { } +package android.view.textclassifier { + + public abstract interface LinksInfo { + method public abstract boolean apply(java.lang.CharSequence); + } + + public final class TextClassificationManager { + method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); + method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + } + + public final class TextClassificationResult { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public android.graphics.drawable.Drawable getIcon(); + method public android.content.Intent getIntent(); + method public java.lang.CharSequence getLabel(); + method public android.view.View.OnClickListener getOnClickListener(); + method public java.lang.String getText(); + } + + public static final class TextClassificationResult.Builder { + ctor public TextClassificationResult.Builder(); + method public android.view.textclassifier.TextClassificationResult build(); + method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float); + method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable); + method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent); + method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String); + method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener); + method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String); + } + + public abstract interface TextClassifier { + method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int); + method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int); + method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int); + field public static final android.view.textclassifier.TextClassifier NO_OP; + field public static final java.lang.String TYPE_ADDRESS = "address"; + field public static final java.lang.String TYPE_EMAIL = "email"; + field public static final java.lang.String TYPE_OTHER = "other"; + field public static final java.lang.String TYPE_PHONE = "phone"; + } + + public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation { + } + + public final class TextLanguage { + method public float getConfidenceScore(java.util.Locale); + method public int getEndIndex(); + method public java.util.Locale getLanguage(int); + method public int getLanguageCount(); + method public int getStartIndex(); + } + + public static final class TextLanguage.Builder { + ctor public TextLanguage.Builder(int, int); + method public android.view.textclassifier.TextLanguage build(); + method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float); + } + + public final class TextSelection { + method public float getConfidenceScore(java.lang.String); + method public java.lang.String getEntity(int); + method public int getEntityCount(); + method public int getSelectionEndIndex(); + method public int getSelectionStartIndex(); + } + + public static final class TextSelection.Builder { + ctor public TextSelection.Builder(int, int); + method public android.view.textclassifier.TextSelection build(); + method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float); + } + +} + package android.view.textservice { public final class SentenceSuggestionsInfo implements android.os.Parcelable { @@ -49872,7 +49935,7 @@ package android.widget { method public float getShadowRadius(); method public final boolean getShowSoftInputOnFocus(); method public java.lang.CharSequence getText(); - method public android.text.TextAssistant getTextAssistant(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public final android.content.res.ColorStateList getTextColors(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); @@ -49988,7 +50051,7 @@ package android.widget { method public final void setText(int, android.widget.TextView.BufferType); method public void setTextAppearance(int); method public deprecated void setTextAppearance(android.content.Context, int); - method public void setTextAssistant(android.text.TextAssistant); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public void setTextColor(int); method public void setTextColor(android.content.res.ColorStateList); method public void setTextIsSelectable(boolean); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 556d7add513f..a9d1cf6e490f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -78,8 +78,6 @@ import android.service.autofill.AutoFillService; import android.service.autofill.IAutoFillAppCallback; import android.text.Selection; import android.text.SpannableStringBuilder; -import android.text.TextAssistant; -import android.text.TextClassificationManager; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.transition.Scene; @@ -792,8 +790,6 @@ public class Activity extends ContextThemeWrapper private VoiceInteractor mVoiceInteractor; - private TextAssistant mTextAssistant; - private CharSequence mTitle; private int mTitleColor = 0; @@ -1398,24 +1394,6 @@ public class Activity extends ContextThemeWrapper } /** - * Sets the default {@link TextAssistant} for {@link android.widget.TextView}s in this Activity. - */ - public void setTextAssistant(TextAssistant textAssistant) { - mTextAssistant = textAssistant; - } - - /** - * Returns the default {@link TextAssistant} for {@link android.widget.TextView}s - * in this Activity. - */ - public TextAssistant getTextAssistant() { - if (mTextAssistant != null) { - return mTextAssistant; - } - return getSystemService(TextClassificationManager.class); - } - - /** * This is called for activities that set launchMode to "singleTop" in * their package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} * flag when calling {@link #startActivity}. In either case, when the diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f909af069224..d674bfe23231 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -47,6 +47,8 @@ interface INotificationManager in Notification notification, inout int[] idReceived, int userId); void cancelNotificationWithTag(String pkg, String tag, int id, int userId); + void setShowBadge(String pkg, int uid, boolean showBadge); + boolean canShowBadge(String pkg, int uid); void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled); boolean areNotificationsEnabledForPackage(String pkg, int uid); boolean areNotificationsEnabled(String pkg); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 6793c90f9be1..c0bf0c46988a 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -24,25 +24,25 @@ import android.app.trust.ITrustManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; -import android.os.PowerManager; import android.os.RemoteException; -import android.os.IBinder; -import android.os.IUserManager; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserHandle; import android.util.Log; -import android.view.IWindowManager; import android.view.IOnKeyguardExitResult; -import android.view.WindowManager; +import android.view.IWindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; import com.android.internal.policy.IKeyguardDismissCallback; +import java.util.List; + /** * Class that can be used to lock and unlock the keyboard. Get an instance of this * class by calling {@link android.content.Context#getSystemService(java.lang.String)} @@ -100,12 +100,9 @@ public class KeyguardManager { Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL); intent.putExtra(EXTRA_TITLE, title); intent.putExtra(EXTRA_DESCRIPTION, description); - if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { - intent.setPackage("com.google.android.apps.wearable.settings"); - } else { - // For security reasons, only allow this to come from system settings. - intent.setPackage("com.android.settings"); - } + + // explicitly set the package for security + intent.setPackage(getSettingsPackageForIntent(intent)); return intent; } @@ -126,15 +123,23 @@ public class KeyguardManager { intent.putExtra(EXTRA_TITLE, title); intent.putExtra(EXTRA_DESCRIPTION, description); intent.putExtra(Intent.EXTRA_USER_ID, userId); - if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { - intent.setPackage("com.google.android.apps.wearable.settings"); - } else { - // For security reasons, only allow this to come from system settings. - intent.setPackage("com.android.settings"); - } + + // explicitly set the package for security + intent.setPackage(getSettingsPackageForIntent(intent)); + return intent; } + private String getSettingsPackageForIntent(Intent intent) { + List<ResolveInfo> resolveInfos = mContext.getPackageManager() + .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY); + for (int i = 0; i < resolveInfos.size(); i++) { + return resolveInfos.get(i).activityInfo.packageName; + } + + return "com.android.settings"; + } + /** * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 601dfceb3a4a..82917d27d0d6 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1051,7 +1051,7 @@ public class Notification implements Parcelable private final Bundle mExtras; private Icon mIcon; private final RemoteInput[] mRemoteInputs; - private boolean mAllowGeneratedReplies = false; + private boolean mAllowGeneratedReplies = true; /** * Small icon representing the action. @@ -1093,7 +1093,7 @@ public class Notification implements Parcelable */ @Deprecated public Action(int icon, CharSequence title, PendingIntent intent) { - this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, false); + this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true); } /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */ @@ -1166,7 +1166,7 @@ public class Notification implements Parcelable private final Icon mIcon; private final CharSequence mTitle; private final PendingIntent mIntent; - private boolean mAllowGeneratedReplies; + private boolean mAllowGeneratedReplies = true; private final Bundle mExtras; private ArrayList<RemoteInput> mRemoteInputs; @@ -1188,7 +1188,7 @@ public class Notification implements Parcelable * @param intent the {@link PendingIntent} to fire when users trigger this action */ public Builder(Icon icon, CharSequence title, PendingIntent intent) { - this(icon, title, intent, new Bundle(), null, false); + this(icon, title, intent, new Bundle(), null, true); } /** @@ -1260,7 +1260,7 @@ public class Notification implements Parcelable * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false} * otherwise * @return this object for method chaining - * The default value is {@code false} + * The default value is {@code true} */ public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) { mAllowGeneratedReplies = allowGeneratedReplies; diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 56ef791d365a..be5f80a82b1b 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -122,6 +122,7 @@ public final class NotificationChannel implements Parcelable { private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; + private static final boolean DEFAULT_SHOW_BADGE = true; private final String mId; private CharSequence mName; @@ -133,7 +134,7 @@ public final class NotificationChannel implements Parcelable { private long[] mVibration; private int mUserLockedFields; private boolean mVibrationEnabled; - private boolean mShowBadge; + private boolean mShowBadge = DEFAULT_SHOW_BADGE; private boolean mDeleted = DEFAULT_DELETED; /** @@ -368,6 +369,8 @@ public final class NotificationChannel implements Parcelable { /** * Returns whether notifications posted to this channel can appear as badges in a Launcher * application. + * + * Note that badging may be disabled for other reasons. */ public boolean canShowBadge() { return mShowBadge; diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java new file mode 100644 index 000000000000..1f015a607be8 --- /dev/null +++ b/core/java/android/app/RecoverableSecurityException.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 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.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Specialization of {@link SecurityException} that contains additional + * information about how to involve the end user to recover from the exception. + * <p> + * This exception is only appropriate where there is a concrete action the user + * can take to recover and make forward progress, such as confirming or entering + * authentication credentials. + * <p class="note"> + * Note: legacy code that receives this exception may treat it as a general + * {@link SecurityException}, and thus there is no guarantee that the messages + * contained will be shown to the end user. + * </p> + */ +public final class RecoverableSecurityException extends SecurityException implements Parcelable { + private static final String TAG = "RecoverableSecurityException"; + + private final CharSequence mUserMessage; + private final CharSequence mUserActionTitle; + private final PendingIntent mUserAction; + + /** {@hide} */ + public RecoverableSecurityException(Parcel in) { + this(new SecurityException(in.readString()), in.readCharSequence(), in.readCharSequence(), + PendingIntent.CREATOR.createFromParcel(in)); + } + + /** + * Create an instance ready to be thrown. + * + * @param cause original cause with details designed for engineering + * audiences. + * @param userMessage short message describing the issue for end user + * audiences, which may be shown in a notification or dialog. + * This should be less than 64 characters. For example: <em>PIN + * required to access Document.pdf</em> + * @param userActionTitle short title describing the primary action. This + * should be less than 24 characters. For example: <em>Enter + * PIN</em> + * @param userAction primary action that will initiate the recovery. This + * must launch an activity that is expected to set + * {@link Activity#setResult(int)} before finishing to + * communicate the final status of the recovery. For example, + * apps that observe {@link Activity#RESULT_OK} may choose to + * immediately retry their operation. + */ + public RecoverableSecurityException(Throwable cause, CharSequence userMessage, + CharSequence userActionTitle, PendingIntent userAction) { + super(cause.getMessage()); + mUserMessage = Preconditions.checkNotNull(userMessage); + mUserActionTitle = Preconditions.checkNotNull(userActionTitle); + mUserAction = Preconditions.checkNotNull(userAction); + } + + /** + * Return short message describing the issue for end user audiences, which + * may be shown in a notification or dialog. + */ + public CharSequence getUserMessage() { + return mUserMessage; + } + + /** + * Return short title describing the primary action. + */ + public CharSequence getUserActionTitle() { + return mUserActionTitle; + } + + /** + * Return primary action that will initiate the recovery. + */ + public PendingIntent getUserAction() { + return mUserAction; + } + + /** + * Convenience method that will show a very simple notification populated + * with the details from this exception. + * <p> + * If you want more flexibility over retrying your original operation once + * the user action has finished, consider presenting your own UI that uses + * {@link Activity#startIntentSenderForResult} to launch the + * {@link PendingIntent#getIntentSender()} from {@link #getUserAction()} + * when requested. If the result of that activity is + * {@link Activity#RESULT_OK}, you should consider retrying. + * <p> + * This method will only display the most recent exception from any single + * remote UID; notifications from older exceptions will always be replaced. + */ + public void showAsNotification(Context context) { + final Notification.Builder builder = new Notification.Builder(context) + .setSmallIcon(com.android.internal.R.drawable.ic_print_error) + .setContentTitle(mUserActionTitle) + .setContentText(mUserMessage) + .setContentIntent(mUserAction) + .setCategory(Notification.CATEGORY_ERROR); + + final NotificationManager nm = context.getSystemService(NotificationManager.class); + nm.notify(TAG, mUserAction.getCreatorUid(), builder.build()); + } + + /** + * Convenience method that will show a very simple dialog populated with the + * details from this exception. + * <p> + * If you want more flexibility over retrying your original operation once + * the user action has finished, consider presenting your own UI that uses + * {@link Activity#startIntentSenderForResult} to launch the + * {@link PendingIntent#getIntentSender()} from {@link #getUserAction()} + * when requested. If the result of that activity is + * {@link Activity#RESULT_OK}, you should consider retrying. + * <p> + * This method will only display the most recent exception from any single + * remote UID; dialogs from older exceptions will always be replaced. + */ + public void showAsDialog(Activity activity) { + final LocalDialog dialog = new LocalDialog(); + final Bundle args = new Bundle(); + args.putParcelable(TAG, this); + dialog.setArguments(args); + + final String tag = TAG + "_" + mUserAction.getCreatorUid(); + final FragmentManager fm = activity.getFragmentManager(); + final FragmentTransaction ft = fm.beginTransaction(); + final Fragment old = fm.findFragmentByTag(tag); + if (old != null) { + ft.remove(old); + } + ft.add(dialog, tag); + ft.commitAllowingStateLoss(); + } + + /** {@hide} */ + public static class LocalDialog extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final RecoverableSecurityException e = getArguments().getParcelable(TAG); + return new AlertDialog.Builder(getActivity()) + .setMessage(e.mUserMessage) + .setPositiveButton(e.mUserActionTitle, (dialog, which) -> { + try { + e.mUserAction.send(); + } catch (PendingIntent.CanceledException ignored) { + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(getMessage()); + dest.writeCharSequence(mUserMessage); + dest.writeCharSequence(mUserActionTitle); + mUserAction.writeToParcel(dest, flags); + } + + public static final Creator<RecoverableSecurityException> CREATOR = + new Creator<RecoverableSecurityException>() { + @Override + public RecoverableSecurityException createFromParcel(Parcel source) { + return new RecoverableSecurityException(source); + } + + @Override + public RecoverableSecurityException[] newArray(int size) { + return new RecoverableSecurityException[size]; + } + }; +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a37f22b888b0..5d8909c1dbae 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -119,7 +119,6 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.FontManager; -import android.text.TextClassificationManager; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -128,6 +127,7 @@ import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; import android.view.inputmethod.InputMethodManager; +import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import com.android.internal.app.IAppOpsService; @@ -228,10 +228,10 @@ final class SystemServiceRegistry { }}); registerService(Context.TEXT_CLASSIFICATION_SERVICE, TextClassificationManager.class, - new StaticServiceFetcher<TextClassificationManager>() { + new CachedServiceFetcher<TextClassificationManager>() { @Override - public TextClassificationManager createService() { - return new TextClassificationManager(); + public TextClassificationManager createService(ContextImpl ctx) { + return new TextClassificationManager(ctx); }}); registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class, diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0da89eb2bd98..aa56be6f36cd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3735,13 +3735,13 @@ public class DevicePolicyManager { } /** - * Called by a device owner to set whether auto time is required. If auto time is required the - * user cannot set the date and time, but has to use network date and time. + * Called by a device or profile owner to set whether auto time is required. If auto time is + * required, no user will be able set the date and time and network date and time will be used. * <p> * Note: if auto time is required the user can still manually set the time zone. * <p> - * The calling device admin must be a device owner. If it is not, a security exception will be - * thrown. + * The calling device admin must be a device or profile owner. If it is not, a security + * exception will be thrown. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param required Whether auto time is set required or not. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f41d7f2f168d..38e6fbeb007f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -34,9 +34,7 @@ import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.IApplicationThread; import android.app.IServiceConnection; -import android.app.LoadedApk; import android.app.Notification; -import android.app.admin.DevicePolicyManager; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; @@ -64,6 +62,7 @@ import android.view.Display; import android.view.DisplayAdjustments; import android.view.ViewDebug; import android.view.WindowManager; +import android.view.textclassifier.TextClassificationManager; import java.io.File; import java.io.FileInputStream; @@ -3348,10 +3347,10 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@link android.text.TextClassificationManager} for text classification services. + * {@link TextClassificationManager} for text classification services. * * @see #getSystemService - * @see android.text.TextClassificationManager + * @see TextClassificationManager */ public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 8cc9a3aecb70..c5500949b25f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4842,6 +4842,10 @@ public class Intent implements Parcelable, Cloneable { * or not running) apps, regardless of whether that would be done by default. By * default they will only receive broadcasts if the broadcast has specified an * explicit component or package name. + * + * NOTE: dumpstate uses this flag numerically, so when its value is changed + * the broadcast code there must also be changed to match. + * * @hide */ public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000; diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 5cd15ddd2faa..b091d7ed4168 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -72,6 +72,11 @@ public class ComponentInfo extends PackageItemInfo { */ public boolean directBootAware = false; + /** + * The name of the split that contains the code for this component. + */ + public String splitName; + /** @removed */ @Deprecated public boolean encryptionAware = false; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d8d7abe6360a..ca3011e0c7fb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3899,6 +3899,9 @@ public class PackageParser { a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, owner.applicationInfo.taskAffinity, str, outError); + a.info.splitName = + sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0); + a.info.flags = 0; if (sa.getBoolean( R.styleable.AndroidManifestActivity_multiprocess, false)) { @@ -4520,6 +4523,9 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_initOrder, 0); + p.info.splitName = + sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0); + p.info.flags = 0; if (sa.getBoolean( @@ -4816,6 +4822,9 @@ public class PackageParser { s.info.permission = str.length() > 0 ? str.toString().intern() : null; } + s.info.splitName = + sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0); + s.info.flags = 0; if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_stopWithTask, diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index b03c9070dc44..35a266b7ab41 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -291,4 +291,6 @@ interface IStorageManager { void fstrim(int flags) = 72; AppFuseMount mountProxyFileDescriptorBridge() = 73; ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74; + long getCacheQuotaBytes(String volumeUuid, int uid) = 75; + long getCacheSizeBytes(String volumeUuid, int uid) = 76; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index c6ff47694bab..626d6f4698ca 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -24,27 +24,32 @@ import android.annotation.SdkConstant; import android.app.ActivityThread; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.IPackageMoveObserver; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; -import android.os.ProxyFileDescriptorCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.ProxyFileDescriptorCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.AppFuseMount; @@ -60,6 +65,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.lang.ref.WeakReference; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1396,6 +1402,222 @@ public class StorageManager { } } + /** + * Return quota size in bytes for cached data belonging to the calling app. + * <p> + * If your app goes above this quota, your cached files will be some of the + * first to be deleted when additional disk space is needed. Conversely, if + * your app stays under this quota, your cached files will be some of the + * last to be deleted when additional disk space is needed. + * <p> + * This quota may change over time depending on how frequently the user + * interacts with your app, and depending on how much disk space is used. + * <p> + * Cached data tracked by this method always includes + * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and + * it also includes {@link Context#getExternalCacheDir()} if the primary + * shared/external storage is hosted on the same storage device as your + * private data. + * <p class="note"> + * Note: if your app uses the {@code android:sharedUserId} manifest feature, + * then cached data for all packages in your shared UID is tracked together + * as a single unit. + * </p> + * + * @see #getCacheQuotaBytes() + * @see #getCacheSizeBytes() + * @see #getExternalCacheQuotaBytes() + * @see #getExternalCacheSizeBytes() + */ + public long getCacheQuotaBytes() { + try { + final ApplicationInfo app = mContext.getApplicationInfo(); + return mStorageManager.getCacheQuotaBytes(app.volumeUuid, app.uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return total size in bytes of cached data belonging to the calling app. + * <p> + * Cached data tracked by this method always includes + * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and + * it also includes {@link Context#getExternalCacheDir()} if the primary + * shared/external storage is hosted on the same storage device as your + * private data. + * <p class="note"> + * Note: if your app uses the {@code android:sharedUserId} manifest feature, + * then cached data for all packages in your shared UID is tracked together + * as a single unit. + * </p> + * + * @see #getCacheQuotaBytes() + * @see #getCacheSizeBytes() + * @see #getExternalCacheQuotaBytes() + * @see #getExternalCacheSizeBytes() + */ + public long getCacheSizeBytes() { + try { + final ApplicationInfo app = mContext.getApplicationInfo(); + return mStorageManager.getCacheSizeBytes(app.volumeUuid, app.uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return quota size in bytes for cached data on primary shared/external + * storage belonging to the calling app. + * <p> + * If primary shared/external storage is hosted on the same storage device + * as your private data, this method will return -1, since all data stored + * under {@link Context#getExternalCacheDir()} will be counted under + * {@link #getCacheQuotaBytes()}. + * <p class="note"> + * Note: if your app uses the {@code android:sharedUserId} manifest feature, + * then cached data for all packages in your shared UID is tracked together + * as a single unit. + * </p> + */ + public long getExternalCacheQuotaBytes() { + final ApplicationInfo app = mContext.getApplicationInfo(); + final String primaryUuid = getPrimaryStorageUuid(); + if (Objects.equals(app.volumeUuid, primaryUuid)) { + return -1; + } + try { + return mStorageManager.getCacheQuotaBytes(primaryUuid, app.uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return total size in bytes of cached data on primary shared/external + * storage belonging to the calling app. + * <p> + * If primary shared/external storage is hosted on the same storage device + * as your private data, this method will return -1, since all data stored + * under {@link Context#getExternalCacheDir()} will be counted under + * {@link #getCacheQuotaBytes()}. + * <p class="note"> + * Note: if your app uses the {@code android:sharedUserId} manifest feature, + * then cached data for all packages in your shared UID is tracked together + * as a single unit. + * </p> + */ + public long getExternalCacheSizeBytes() { + final ApplicationInfo app = mContext.getApplicationInfo(); + final String primaryUuid = getPrimaryStorageUuid(); + if (Objects.equals(app.volumeUuid, primaryUuid)) { + return -1; + } + try { + return mStorageManager.getCacheSizeBytes(primaryUuid, app.uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static final String XATTR_ATOMIC = "user.atomic"; + private static final String XATTR_TOMBSTONE = "user.tombstone"; + + /** {@hide} */ + private static void setCacheBehavior(File path, String name, boolean enabled) + throws IOException { + if (!path.isDirectory()) { + throw new IOException("Cache behavior can only be set on directories"); + } + if (enabled) { + try { + Os.setxattr(path.getAbsolutePath(), name, + "1".getBytes(StandardCharsets.UTF_8), 0); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } else { + try { + Os.removexattr(path.getAbsolutePath(), name); + } catch (ErrnoException e) { + if (e.errno != OsConstants.ENODATA) { + throw e.rethrowAsIOException(); + } + } + } + } + + /** {@hide} */ + private static boolean isCacheBehavior(File path, String name) throws IOException { + try { + Os.getxattr(path.getAbsolutePath(), name); + return true; + } catch (ErrnoException e) { + if (e.errno != OsConstants.ENODATA) { + throw e.rethrowAsIOException(); + } else { + return false; + } + } + } + + /** + * Enable or disable special cache behavior that treats this directory and + * its contents as an atomic unit. + * <p> + * When enabled and this directory is considered for automatic deletion by + * the OS, all contained files will either be deleted together, or not at + * all. This is useful when you have a directory that contains several + * related metadata files that depend on each other, such as movie file and + * a subtitle file. + * <p> + * When enabled, the <em>newest</em> {@link File#lastModified()} value of + * any contained files is considered the modified time of the entire + * directory. + * <p> + * This behavior can only be set on a directory, and it applies recursively + * to all contained files and directories. + */ + public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException { + setCacheBehavior(path, XATTR_ATOMIC, atomic); + } + + /** + * Read the current value set by + * {@link #setCacheBehaviorAtomic(File, boolean)}. + */ + public boolean isCacheBehaviorAtomic(File path) throws IOException { + return isCacheBehavior(path, XATTR_ATOMIC); + } + + /** + * Enable or disable special cache behavior that leaves deleted cache files + * intact as tombstones. + * <p> + * When enabled and a file contained in this directory is automatically + * deleted by the OS, the file will be truncated to have a length of 0 bytes + * instead of being fully deleted. This is useful if you need to distinguish + * between a file that was deleted versus one that never existed. + * <p> + * This behavior can only be set on a directory, and it applies recursively + * to all contained files and directories. + * <p class="note"> + * Note: this behavior is ignored completely if the user explicitly requests + * that all cached data be cleared. + * </p> + */ + public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException { + setCacheBehavior(path, XATTR_TOMBSTONE, tombstone); + } + + /** + * Read the current value set by + * {@link #setCacheBehaviorTombstone(File, boolean)}. + */ + public boolean isCacheBehaviorTombstone(File path) throws IOException { + return isCacheBehavior(path, XATTR_TOMBSTONE); + } + private final Object mFuseAppLoopLock = new Object(); @GuardedBy("mFuseAppLoopLock") diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 694837e53c73..d9306897d876 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1166,11 +1166,12 @@ public abstract class NotificationListenerService extends Service { // System specified group key. private String mOverrideGroupKey; // Notification assistant channel override. - private NotificationChannel mOverrideChannel; + private NotificationChannel mChannel; // Notification assistant people override. private ArrayList<String> mOverridePeople; // Notification assistant snooze criteria. private ArrayList<SnoozeCriterion> mSnoozeCriteria; + private boolean mShowBadge; public Ranking() {} @@ -1200,7 +1201,7 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns the user specificed visibility for the package that posted + * Returns the user specified visibility for the package that posted * this notification, or * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if * no such preference has been expressed. @@ -1233,7 +1234,7 @@ public abstract class NotificationListenerService extends Service { * Returns the importance of the notification, which dictates its * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc. * - * @return the rank of the notification + * @return the importance of the notification */ public @NotificationManager.Importance int getImportance() { return mImportance; @@ -1258,12 +1259,11 @@ public abstract class NotificationListenerService extends Service { } /** - * If the {@link NotificationAssistantService} has overridden the channel this notification - * was posted to, then this will not match the channel provided by the posting application - * and this should be used to determine the interruptiveness of the notification instead. + * Returns the notification channel this notification was posted to, which dictates + * notification behavior and presentation. */ public NotificationChannel getChannel() { - return mOverrideChannel; + return mChannel; } /** @@ -1283,11 +1283,20 @@ public abstract class NotificationListenerService extends Service { return mSnoozeCriteria; } + /** + * Returns whether this notification can be displayed as a badge. + * + * @return true if the notification can be displayed as a badge, false otherwise. + */ + public boolean canShowBadge() { + return mShowBadge; + } + private void populate(String key, int rank, boolean matchesInterruptionFilter, int visibilityOverride, int suppressedVisualEffects, int importance, CharSequence explanation, String overrideGroupKey, - NotificationChannel overrideChannel, ArrayList<String> overridePeople, - ArrayList<SnoozeCriterion> snoozeCriteria) { + NotificationChannel channel, ArrayList<String> overridePeople, + ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1297,9 +1306,10 @@ public abstract class NotificationListenerService extends Service { mImportance = importance; mImportanceExplanation = explanation; mOverrideGroupKey = overrideGroupKey; - mOverrideChannel = overrideChannel; + mChannel = channel; mOverridePeople = overridePeople; mSnoozeCriteria = snoozeCriteria; + mShowBadge = showBadge; } /** @@ -1343,9 +1353,10 @@ public abstract class NotificationListenerService extends Service { private ArrayMap<String, Integer> mImportance; private ArrayMap<String, String> mImportanceExplanation; private ArrayMap<String, String> mOverrideGroupKeys; - private ArrayMap<String, NotificationChannel> mOverrideChannels; + private ArrayMap<String, NotificationChannel> mChannels; private ArrayMap<String, ArrayList<String>> mOverridePeople; private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria; + private ArrayMap<String, Boolean> mShowBadge; private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; @@ -1373,7 +1384,8 @@ public abstract class NotificationListenerService extends Service { outRanking.populate(key, rank, !isIntercepted(key), getVisibilityOverride(key), getSuppressedVisualEffects(key), getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), - getOverrideChannel(key), getOverridePeople(key), getSnoozeCriteria(key)); + getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), + getShowBadge(key)); return rank >= 0; } @@ -1453,13 +1465,13 @@ public abstract class NotificationListenerService extends Service { return mOverrideGroupKeys.get(key); } - private NotificationChannel getOverrideChannel(String key) { + private NotificationChannel getChannel(String key) { synchronized (this) { - if (mOverrideChannels == null) { - buildOverrideChannelsLocked(); + if (mChannels == null) { + buildChannelsLocked(); } } - return mOverrideChannels.get(key); + return mChannels.get(key); } private ArrayList<String> getOverridePeople(String key) { @@ -1480,6 +1492,16 @@ public abstract class NotificationListenerService extends Service { return mSnoozeCriteria.get(key); } + private boolean getShowBadge(String key) { + synchronized (this) { + if (mShowBadge == null) { + buildShowBadgeLocked(); + } + } + Boolean showBadge = mShowBadge.get(key); + return showBadge == null ? false : showBadge.booleanValue(); + } + // Locked by 'this' private void buildRanksLocked() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); @@ -1544,11 +1566,11 @@ public abstract class NotificationListenerService extends Service { } // Locked by 'this' - private void buildOverrideChannelsLocked() { - Bundle overrideChannels = mRankingUpdate.getOverrideChannels(); - mOverrideChannels = new ArrayMap<>(overrideChannels.size()); - for (String key : overrideChannels.keySet()) { - mOverrideChannels.put(key, overrideChannels.getParcelable(key)); + private void buildChannelsLocked() { + Bundle channels = mRankingUpdate.getChannels(); + mChannels = new ArrayMap<>(channels.size()); + for (String key : channels.keySet()) { + mChannels.put(key, channels.getParcelable(key)); } } @@ -1570,6 +1592,15 @@ public abstract class NotificationListenerService extends Service { } } + // Locked by 'this' + private void buildShowBadgeLocked() { + Bundle showBadge = mRankingUpdate.getShowBadge(); + mShowBadge = new ArrayMap<>(showBadge.size()); + for (String key : showBadge.keySet()) { + mShowBadge.put(key, showBadge.getBoolean(key)); + } + } + // ----------- Parcelable @Override diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index a2cdeffef2b1..326b212a9417 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -31,14 +31,16 @@ public class NotificationRankingUpdate implements Parcelable { private final int[] mImportance; private final Bundle mImportanceExplanation; private final Bundle mOverrideGroupKeys; - private final Bundle mOverrideChannels; + private final Bundle mChannels; private final Bundle mOverridePeople; private final Bundle mSnoozeCriteria; + private final Bundle mShowBadge; public NotificationRankingUpdate(String[] keys, String[] interceptedKeys, Bundle visibilityOverrides, Bundle suppressedVisualEffects, int[] importance, Bundle explanation, Bundle overrideGroupKeys, - Bundle overrideChannels, Bundle overridePeople, Bundle snoozeCriteria) { + Bundle channels, Bundle overridePeople, Bundle snoozeCriteria, + Bundle showBadge) { mKeys = keys; mInterceptedKeys = interceptedKeys; mVisibilityOverrides = visibilityOverrides; @@ -46,9 +48,10 @@ public class NotificationRankingUpdate implements Parcelable { mImportance = importance; mImportanceExplanation = explanation; mOverrideGroupKeys = overrideGroupKeys; - mOverrideChannels = overrideChannels; + mChannels = channels; mOverridePeople = overridePeople; mSnoozeCriteria = snoozeCriteria; + mShowBadge = showBadge; } public NotificationRankingUpdate(Parcel in) { @@ -60,9 +63,10 @@ public class NotificationRankingUpdate implements Parcelable { in.readIntArray(mImportance); mImportanceExplanation = in.readBundle(); mOverrideGroupKeys = in.readBundle(); - mOverrideChannels = in.readBundle(); + mChannels = in.readBundle(); mOverridePeople = in.readBundle(); mSnoozeCriteria = in.readBundle(); + mShowBadge = in.readBundle(); } @Override @@ -79,9 +83,10 @@ public class NotificationRankingUpdate implements Parcelable { out.writeIntArray(mImportance); out.writeBundle(mImportanceExplanation); out.writeBundle(mOverrideGroupKeys); - out.writeBundle(mOverrideChannels); + out.writeBundle(mChannels); out.writeBundle(mOverridePeople); out.writeBundle(mSnoozeCriteria); + out.writeBundle(mShowBadge); } public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR @@ -123,8 +128,8 @@ public class NotificationRankingUpdate implements Parcelable { return mOverrideGroupKeys; } - public Bundle getOverrideChannels() { - return mOverrideChannels; + public Bundle getChannels() { + return mChannels; } public Bundle getOverridePeople() { @@ -134,4 +139,8 @@ public class NotificationRankingUpdate implements Parcelable { public Bundle getSnoozeCriteria() { return mSnoozeCriteria; } + + public Bundle getShowBadge() { + return mShowBadge; + } } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 6276af398c43..85baf4edaeca 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -43,21 +43,18 @@ public class StatusBarNotification implements Parcelable { private final Notification notification; private final UserHandle user; private final long postTime; - private final NotificationChannel channel; private Context mContext; // used for inflation & icon expansion /** @hide */ - public StatusBarNotification(String pkg, String opPkg, NotificationChannel channel, int id, + public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, int initialPid, Notification notification, UserHandle user, String overrideGroupKey, long postTime) { if (pkg == null) throw new NullPointerException(); if (notification == null) throw new NullPointerException(); - if (channel == null) throw new IllegalArgumentException(); this.pkg = pkg; this.opPkg = opPkg; - this.channel = channel; this.id = id; this.tag = tag; this.uid = uid; @@ -88,7 +85,6 @@ public class StatusBarNotification implements Parcelable { this.postTime = postTime; this.key = key(); this.groupKey = groupKey(); - this.channel = null; } public StatusBarNotification(Parcel in) { @@ -112,7 +108,6 @@ public class StatusBarNotification implements Parcelable { } this.key = key(); this.groupKey = groupKey(); - this.channel = NotificationChannel.CREATOR.createFromParcel(in); } private String key() { @@ -182,7 +177,6 @@ public class StatusBarNotification implements Parcelable { } else { out.writeInt(0); } - this.channel.writeToParcel(out, flags); } public int describeContents() { @@ -209,14 +203,14 @@ public class StatusBarNotification implements Parcelable { public StatusBarNotification cloneLight() { final Notification no = new Notification(); this.notification.cloneInto(no, false); // light copy - return new StatusBarNotification(this.pkg, this.opPkg, this.channel, + return new StatusBarNotification(this.pkg, this.opPkg, this.id, this.tag, this.uid, this.initialPid, no, this.user, this.overrideGroupKey, this.postTime); } @Override public StatusBarNotification clone() { - return new StatusBarNotification(this.pkg, this.opPkg, this.channel, + return new StatusBarNotification(this.pkg, this.opPkg, this.id, this.tag, this.uid, this.initialPid, this.notification.clone(), this.user, this.overrideGroupKey, this.postTime); } @@ -336,13 +330,6 @@ public class StatusBarNotification implements Parcelable { } /** - * Returns the channel this notification was posted to. - */ - public NotificationChannel getNotificationChannel() { - return channel; - } - - /** * @hide */ public Context getPackageContext(Context context) { diff --git a/core/java/android/text/TextAssistant.java b/core/java/android/text/TextAssistant.java deleted file mode 100644 index b044981cdd95..000000000000 --- a/core/java/android/text/TextAssistant.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2016 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.text; - -/** - * Interface for providing text assistant features. - */ -public interface TextAssistant { - - /** - * NO_OP TextAssistant. This will act as the default TextAssistant until we implement a - * TextClassificationManager. - * @hide - */ - TextAssistant NO_OP = new TextAssistant() { - - private final TextSelection mTextSelection = new TextSelection(); - - @Override - public TextSelection suggestSelection( - CharSequence text, int selectionStartIndex, int selectionEndIndex) { - mTextSelection.mStartIndex = selectionStartIndex; - mTextSelection.mEndIndex = selectionEndIndex; - return mTextSelection; - } - - @Override - public void addLinks(Spannable text, int linkMask) {} - }; - - /** - * Returns suggested text selection indices, recognized types and their associated confidence - * scores. The selections are ordered from highest to lowest scoring. - */ - TextSelection suggestSelection( - CharSequence text, int selectionStartIndex, int selectionEndIndex); - - /** - * Adds assistance clickable spans to the provided text. - */ - void addLinks(Spannable text, int linkMask); -} diff --git a/core/java/android/text/TextClassification.java b/core/java/android/text/TextClassification.java deleted file mode 100644 index bb226daaaeb0..000000000000 --- a/core/java/android/text/TextClassification.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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.text; - -import java.util.Collections; -import java.util.Map; - -/** - * Information about entities that a specific piece of text is classified as. - */ -public class TextClassification { - - /** @hide */ - public static final TextClassification NO_OP = new TextClassification(); - - private Map<String, Float> mTypeConfidence = Collections.unmodifiableMap(Collections.EMPTY_MAP); - - /** - * Returns a map of text classification types to their respective confidence scores. - * The scores range from 0 (low confidence) to 1 (high confidence). The items are ordered from - * high scoring items to low scoring items. - */ - public Map<String, Float> getTypeConfidence() { - return mTypeConfidence; - } -} diff --git a/core/java/android/text/TextClassificationManager.java b/core/java/android/text/TextClassificationManager.java deleted file mode 100644 index d4548f04e0a0..000000000000 --- a/core/java/android/text/TextClassificationManager.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016 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.text; - -import android.annotation.NonNull; - -import java.util.Collections; -import java.util.List; - -/** - * Interface to the text classification service. - * This class uses machine learning techniques to infer things about text. - * Unless otherwise stated, methods of this class are blocking operations and should most likely not - * be called on the UI thread. - * - * <p> You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService}. - * - * The TextClassificationManager serves as the default TextAssistant if none has been set. - * @see android.app.Activity#setTextAssistant(TextAssistant). - */ -public final class TextClassificationManager implements TextAssistant { - // TODO: Consider not making this class implement TextAssistant. - - /** @hide */ - public TextClassificationManager() {} - - /** - * Returns information containing languages that were detected in the provided text. - * This is a blocking operation and should most likely not be called on the UI thread. - */ - public List<TextLanguage> detectLanguages(@NonNull CharSequence text) { - // TODO: Implement this using the cld3 library. - return Collections.emptyList(); - } - - @Override - public TextSelection suggestSelection( - @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) { - // TODO: Implement. - return TextAssistant.NO_OP.suggestSelection(text, selectionStartIndex, selectionEndIndex); - } - - @Override - public void addLinks(@NonNull Spannable text, int linkMask) { - // TODO: Implement. - } -} diff --git a/core/java/android/text/TextLanguage.java b/core/java/android/text/TextLanguage.java deleted file mode 100644 index eb834f120dee..000000000000 --- a/core/java/android/text/TextLanguage.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2016 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.text; - -import android.annotation.NonNull; - -import com.android.internal.util.Preconditions; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Specifies detected languages for a section of text indicated by a start and end index. - */ -public final class TextLanguage { - - private final int mStartIndex; - private final int mEndIndex; - private final Map<String, Float> mLanguageConfidence; - - /** - * Initializes a TextLanguage object. - * - * @param startIndex the start index of the detected languages in the text provided to generate - * this object. - * @param endIndex the end index of the detected languages in the text provided to generate this - * object. - * @param languageConfidence a map of detected language to confidence score. The language string - * is a BCP-47 language tag. - * @throws NullPointerException if languageConfidence is null or contains a null key or value. - */ - public TextLanguage(int startIndex, int endIndex, - @NonNull Map<String, Float> languageConfidence) { - mStartIndex = startIndex; - mEndIndex = endIndex; - - Map<String, Float> map = new LinkedHashMap<>(); - Preconditions.checkNotNull(languageConfidence).entrySet().stream() - .sorted(Map.Entry.comparingByValue()) - .forEach(entry -> map.put( - Preconditions.checkNotNull(entry.getKey()), - Preconditions.checkNotNull(entry.getValue()))); - mLanguageConfidence = Collections.unmodifiableMap(map); - } - - /** - * Returns the start index of the detected languages in the text provided to generate this - * object. - */ - public int getStartIndex() { - return mStartIndex; - } - - /** - * Returns the end index of the detected languages in the text provided to generate this object. - */ - public int getEndIndex() { - return mEndIndex; - } - - /** - * Returns an unmodifiable map of detected language to confidence score. The map entries are - * ordered from high confidence score (1) to low confidence score (0). The language string is a - * BCP-47 language tag. - */ - @NonNull - public Map<String, Float> getLanguageConfidence() { - return mLanguageConfidence; - } -} diff --git a/core/java/android/text/TextSelection.java b/core/java/android/text/TextSelection.java deleted file mode 100644 index 9400458582c7..000000000000 --- a/core/java/android/text/TextSelection.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2016 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.text; - -/** - * Text selection information. - */ -public class TextSelection { - - /** @hide */ - int mStartIndex; - /** @hide */ - int mEndIndex; - - private TextClassification mTextClassification = TextClassification.NO_OP; - - /** - * Returns the start index of the text selection. - */ - public int getSelectionStartIndex() { - return mStartIndex; - } - - /** - * Returns the end index of the text selection. - */ - public int getSelectionEndIndex() { - return mEndIndex; - } - - /** - * Returns information about what the text selection is classified as. - */ - public TextClassification getTextClassification() { - return mTextClassification; - } -} diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index b37ea8ebeaa0..105cc47c88aa 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -292,7 +292,7 @@ public final class Display { public static final int STATE_VR = 5; /* The color mode constants defined below must be kept in sync with the ones in - * system/graphics.h */ + * system/core/include/system/graphics-base.h */ /** * Display color mode: The current color mode is unknown or invalid. @@ -306,11 +306,24 @@ public final class Display { */ public static final int COLOR_MODE_DEFAULT = 0; - /** - * Display color mode: SRGB - * @hide - */ + /** @hide */ + public static final int COLOR_MODE_BT601_625 = 1; + /** @hide */ + public static final int COLOR_MODE_BT601_625_UNADJUSTED = 2; + /** @hide */ + public static final int COLOR_MODE_BT601_525 = 3; + /** @hide */ + public static final int COLOR_MODE_BT601_525_UNADJUSTED = 4; + /** @hide */ + public static final int COLOR_MODE_BT709 = 5; + /** @hide */ + public static final int COLOR_MODE_DCI_P3 = 6; + /** @hide */ public static final int COLOR_MODE_SRGB = 7; + /** @hide */ + public static final int COLOR_MODE_ADOBE_RGB = 8; + /** @hide */ + public static final int COLOR_MODE_DISPLAY_P3 = 9; /** * Internal method to create a display. @@ -745,6 +758,8 @@ public final class Display { /** * Returns the display's HDR capabilities. + * + * @see #isHdr() */ public HdrCapabilities getHdrCapabilities() { synchronized (this) { @@ -754,6 +769,35 @@ public final class Display { } /** + * Returns whether this display supports any HDR type. + * + * @see #getHdrCapabilities() + * @see HdrCapabilities#getSupportedHdrTypes() + */ + public boolean isHdr() { + synchronized (this) { + updateDisplayInfoLocked(); + int[] types = mDisplayInfo.hdrCapabilities.getSupportedHdrTypes(); + return types != null && types.length > 0; + } + } + + /** + * Returns whether this display can be used to display wide color gamut content. + */ + public boolean isWideColorGamut() { + synchronized (this) { + updateDisplayInfoLocked(); + for (int colorMode : mDisplayInfo.supportedColorModes) { + if (colorMode == COLOR_MODE_DCI_P3 || colorMode > COLOR_MODE_SRGB) { + return true; + } + } + return false; + } + } + + /** * Gets the supported color modes of this device. * @hide */ diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index a07a7ef600bf..41a13cf59bd8 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -16,16 +16,12 @@ package android.view; -import static android.view.View.KEYBOARD_NAVIGATION_GROUP_CLUSTER; -import static android.view.View.KEYBOARD_NAVIGATION_GROUP_SECTION; - import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.util.ArrayMap; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.view.View.KeyboardNavigationGroupType; import java.util.ArrayList; import java.util.Collections; @@ -110,31 +106,28 @@ public class FocusFinder { } /** - * Find the root of the next keyboard navigation group after the current one. The group type can - * be either a cluster or a section. - * @param groupType Type of the keyboard navigation group + * Find the root of the next keyboard navigation cluster after the current one. * @param root The view tree to look inside. Cannot be null - * @param currentGroup The starting point of the search. Null means the default group + * @param currentCluster The starting point of the search. Null means the default cluster * @param direction Direction to look - * @return The next group, or null if none exists + * @return The next cluster, or null if none exists */ - public View findNextKeyboardNavigationGroup( - @KeyboardNavigationGroupType int groupType, + public View findNextKeyboardNavigationCluster( @NonNull View root, - @Nullable View currentGroup, + @Nullable View currentCluster, int direction) { View next = null; - final ArrayList<View> groups = mTempList; + final ArrayList<View> clusters = mTempList; try { - groups.clear(); - root.addKeyboardNavigationGroups(groupType, groups, direction); - if (!groups.isEmpty()) { - next = findNextKeyboardNavigationGroup( - groupType, root, currentGroup, groups, direction); + clusters.clear(); + root.addKeyboardNavigationClusters(clusters, direction); + if (!clusters.isEmpty()) { + next = findNextKeyboardNavigationCluster( + root, currentCluster, clusters, direction); } } finally { - groups.clear(); + clusters.clear(); } return next; } @@ -207,25 +200,22 @@ public class FocusFinder { } } - private View findNextKeyboardNavigationGroup( - @KeyboardNavigationGroupType int groupType, + private View findNextKeyboardNavigationCluster( View root, - View currentGroup, - List<View> groups, + View currentCluster, + List<View> clusters, int direction) { - final int count = groups.size(); + final int count = clusters.size(); switch (direction) { case View.FOCUS_FORWARD: case View.FOCUS_DOWN: case View.FOCUS_RIGHT: - return getNextKeyboardNavigationGroup( - groupType, root, currentGroup, groups, count); + return getNextKeyboardNavigationCluster(root, currentCluster, clusters, count); case View.FOCUS_BACKWARD: case View.FOCUS_UP: case View.FOCUS_LEFT: - return getPreviousKeyboardNavigationGroup( - groupType, root, currentGroup, groups, count); + return getPreviousKeyboardNavigationCluster(root, currentCluster, clusters, count); default: throw new IllegalArgumentException("Unknown direction: " + direction); } @@ -331,70 +321,50 @@ public class FocusFinder { return null; } - private static View getNextKeyboardNavigationGroup( - @KeyboardNavigationGroupType int groupType, + private static View getNextKeyboardNavigationCluster( View root, - View currentGroup, - List<View> groups, + View currentCluster, + List<View> clusters, int count) { - if (currentGroup == null) { - // The current group is the default one. - // The next group after the default one is the first one. - // Note that the caller guarantees that 'group' is not empty. - return groups.get(0); + if (currentCluster == null) { + // The current cluster is the default one. + // The next cluster after the default one is the first one. + // Note that the caller guarantees that 'clusters' is not empty. + return clusters.get(0); } - final int position = groups.lastIndexOf(currentGroup); + final int position = clusters.lastIndexOf(currentCluster); if (position >= 0 && position + 1 < count) { - // Return the next non-default group if we can find it. - return groups.get(position + 1); - } - - switch (groupType) { - case KEYBOARD_NAVIGATION_GROUP_CLUSTER: - // The current cluster is the last one. The next one is the default one, i.e. the - // root. - return root; - case KEYBOARD_NAVIGATION_GROUP_SECTION: - // There is no "default section", hence returning the first one. - return groups.get(0); - default: - throw new IllegalArgumentException( - "Unknown keyboard navigation group type: " + groupType); + // Return the next non-default cluster if we can find it. + return clusters.get(position + 1); } + + // The current cluster is the last one. The next one is the default one, i.e. the + // root. + return root; } - private static View getPreviousKeyboardNavigationGroup( - @KeyboardNavigationGroupType int groupType, + private static View getPreviousKeyboardNavigationCluster( View root, - View currentGroup, - List<View> groups, + View currentCluster, + List<View> clusters, int count) { - if (currentGroup == null) { - // The current group is the default one. - // The previous group before the default one is the last one. - // Note that the caller guarantees that 'groups' is not empty. - return groups.get(count - 1); + if (currentCluster == null) { + // The current cluster is the default one. + // The previous cluster before the default one is the last one. + // Note that the caller guarantees that 'clusters' is not empty. + return clusters.get(count - 1); } - final int position = groups.indexOf(currentGroup); + final int position = clusters.indexOf(currentCluster); if (position > 0) { - // Return the previous non-default group if we can find it. - return groups.get(position - 1); - } - - switch (groupType) { - case KEYBOARD_NAVIGATION_GROUP_CLUSTER: - // The current cluster is the first one. The previous one is the default one, i.e. - // the root. - return root; - case KEYBOARD_NAVIGATION_GROUP_SECTION: - // There is no "default section", hence returning the last one. - return groups.get(count - 1); - default: - throw new IllegalArgumentException( - "Unknown keyboard navigation group type: " + groupType); + // Return the previous non-default cluster if we can find it. + return clusters.get(position - 1); } + + // The current cluster is the first one. The previous one is the default one, i.e. + // the root. + return root; } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 26e311c6a921..13555f47c2c5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1252,14 +1252,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Retention(RetentionPolicy.SOURCE) public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward - /** @hide */ - @IntDef({ - KEYBOARD_NAVIGATION_GROUP_CLUSTER, - KEYBOARD_NAVIGATION_GROUP_SECTION - }) - @Retention(RetentionPolicy.SOURCE) - public @interface KeyboardNavigationGroupType {} - /** * Use with {@link #focusSearch(int)}. Move focus to the previous selectable * item. @@ -1293,18 +1285,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int FOCUS_DOWN = 0x00000082; /** - * Use with {@link #keyboardNavigationGroupSearch(int, View, int)}. Search for a keyboard - * navigation cluster. - */ - public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; - - /** - * Use with {@link #keyboardNavigationGroupSearch(int, View, int)}. Search for a keyboard - * navigation section. - */ - public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; - - /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. */ @@ -2500,7 +2480,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_SCROLL_INDICATOR_END * 1 PFLAG3_ASSIST_BLOCKED * 1 PFLAG3_CLUSTER - * 1 PFLAG3_SECTION + * x * NO LONGER NEEDED, SHOULD BE REUSED * * 1 PFLAG3_FINGER_DOWN * 1 PFLAG3_FOCUSED_BY_DEFAULT * xxxx * NO LONGER NEEDED, SHOULD BE REUSED * @@ -2710,14 +2690,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int PFLAG3_CLUSTER = 0x8000; /** - * Flag indicating that the view is a root of a keyboard navigation section. - * - * @see #isKeyboardNavigationSection() - * @see #setKeyboardNavigationSection(boolean) - */ - private static final int PFLAG3_SECTION = 0x10000; - - /** * Indicates that the user is currently touching the screen. * Currently used for the tooltip positioning only. */ @@ -3807,11 +3779,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ int mNextClusterForwardId = View.NO_ID; - /** - * User-specified next keyboard navigation section. - */ - int mNextSectionForwardId = View.NO_ID; - private CheckForLongPress mPendingCheckForLongPress; private CheckForTap mPendingCheckForTap = null; private PerformClick mPerformClick; @@ -4622,9 +4589,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case R.styleable.View_nextClusterForward: mNextClusterForwardId = a.getResourceId(attr, View.NO_ID); break; - case R.styleable.View_nextSectionForward: - mNextSectionForwardId = a.getResourceId(attr, View.NO_ID); - break; case R.styleable.View_minWidth: mMinWidth = a.getDimensionPixelSize(attr, 0); break; @@ -4769,11 +4733,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setKeyboardNavigationCluster(a.getBoolean(attr, true)); } break; - case R.styleable.View_keyboardNavigationSection: - if (a.peekValue(attr) != null) { - setKeyboardNavigationSection(a.getBoolean(attr, true)); - } - break; case R.styleable.View_focusedByDefault: if (a.peekValue(attr) != null) { setFocusedByDefault(a.getBoolean(attr, true)); @@ -8043,28 +8002,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the id of the root of the next keyboard navigation section. - * @return The next keyboard navigation section ID, or {@link #NO_ID} if the framework should - * decide automatically. - * - * @attr ref android.R.styleable#View_nextSectionForward - */ - public int getNextSectionForwardId() { - return mNextSectionForwardId; - } - - /** - * Sets the id of the view to use as the root of the next keyboard navigation section. - * @param nextSectionForwardId The next section ID, or {@link #NO_ID} if the framework should - * decide automatically. - * - * @attr ref android.R.styleable#View_nextSectionForward - */ - public void setNextSectionForwardId(int nextSectionForwardId) { - mNextSectionForwardId = nextSectionForwardId; - } - - /** * Returns the visibility of this view and all of its ancestors * * @return True if this view and all of its ancestors are {@link #VISIBLE} @@ -9186,49 +9123,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Returns whether this View is a root of a keyboard navigation section. - * - * @return True if this view is a root of a section, or false otherwise. - * @attr ref android.R.styleable#View_keyboardNavigationSection - */ - @ViewDebug.ExportedProperty(category = "keyboardNavigationSection") - public final boolean isKeyboardNavigationSection() { - return (mPrivateFlags3 & PFLAG3_SECTION) != 0; - } - - /** - * Set whether this view is a root of a keyboard navigation section. - * - * @param isSection If true, this view is a root of a section. - * - * @attr ref android.R.styleable#View_keyboardNavigationSection - */ - public void setKeyboardNavigationSection(boolean isSection) { - if (isSection) { - mPrivateFlags3 |= PFLAG3_SECTION; - } else { - mPrivateFlags3 &= ~PFLAG3_SECTION; - } - } - - final boolean isKeyboardNavigationGroupOfType(@KeyboardNavigationGroupType int groupType) { - switch (groupType) { - case KEYBOARD_NAVIGATION_GROUP_CLUSTER: - return isKeyboardNavigationCluster(); - case KEYBOARD_NAVIGATION_GROUP_SECTION: - return isKeyboardNavigationSection(); - default: - throw new IllegalArgumentException( - "Unknown keyboard navigation group type: " + groupType); - } - } - - /** * Returns whether this View should receive focus when the focus is restored for the view * hierarchy containing this view. * <p> * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a - * window or serves as a target of cluster or section navigation. + * window or serves as a target of cluster navigation. * * @see #restoreDefaultFocus(int) * @@ -9245,7 +9144,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * hierarchy containing this view. * <p> * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a - * window or serves as a target of cluster or section navigation. + * window or serves as a target of cluster navigation. * * @param isFocusedByDefault {@code true} to set this view as the default-focus view, * {@code false} otherwise. @@ -9284,35 +9183,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Find the nearest keyboard navigation group in the specified direction. The group type can be - * either a cluster or a section. - * This does not actually give focus to that group. + * Find the nearest keyboard navigation cluster in the specified direction. + * This does not actually give focus to that cluster. * - * @param groupType Type of the keyboard navigation group - * @param currentGroup The starting point of the search. Null means the current group is not - * found yet + * @param currentCluster The starting point of the search. Null means the current cluster is not + * found yet * @param direction Direction to look * - * @return The nearest keyboard navigation group in the specified direction, or null if none + * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - public View keyboardNavigationGroupSearch( - @KeyboardNavigationGroupType int groupType, View currentGroup, int direction) { - if (isKeyboardNavigationGroupOfType(groupType)) { - currentGroup = this; + public View keyboardNavigationClusterSearch(View currentCluster, int direction) { + if (isKeyboardNavigationCluster()) { + currentCluster = this; } - if (isRootNamespace() - || (groupType == KEYBOARD_NAVIGATION_GROUP_SECTION - && isKeyboardNavigationCluster())) { + if (isRootNamespace()) { // Root namespace means we should consider ourselves the top of the // tree for group searching; otherwise we could be group searching // into other tabs. see LocalActivityManager and TabHost for more info. - // In addition, a cluster node works as a root for section searches. - return FocusFinder.getInstance().findNextKeyboardNavigationGroup( - groupType, this, currentGroup, direction); + return FocusFinder.getInstance().findNextKeyboardNavigationCluster( + this, currentCluster, direction); } else if (mParent != null) { - return mParent.keyboardNavigationGroupSearch( - groupType, currentGroup, direction); + return mParent.keyboardNavigationClusterSearch(currentCluster, direction); } return null; } @@ -9440,19 +9332,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Adds any keyboard navigation group roots that are descendants of this view (possibly - * including this view if it is a group root itself) to views. The group type can be either a - * cluster or a section. + * Adds any keyboard navigation cluster roots that are descendants of this view (possibly + * including this view if it is a cluster root itself) to views. * - * @param groupType Type of the keyboard navigation group - * @param views Keyboard navigation group roots found so far + * @param views Keyboard navigation cluster roots found so far * @param direction Direction to look */ - public void addKeyboardNavigationGroups( - @KeyboardNavigationGroupType int groupType, + public void addKeyboardNavigationClusters( @NonNull Collection<View> views, int direction) { - if (!(isKeyboardNavigationGroupOfType(groupType))) { + if (!(isKeyboardNavigationCluster())) { return; } views.add(this); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d252d75856da..480741eb988a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -915,13 +915,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public View focusSearch(View focused, int direction) { - if (isRootNamespace() - || isKeyboardNavigationCluster() - && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD)) { + if (isRootNamespace()) { // root namespace means we should consider ourselves the top of the // tree for focus searching; otherwise we could be focus searching // into other tabs. see LocalActivityManager and TabHost for more info. - // Cluster's root works same way for the forward and backward navigation. return FocusFinder.getInstance().findNextFocus(this, focused, direction); } else if (mParent != null) { return mParent.focusSearch(focused, direction); @@ -1136,12 +1133,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { - if (isKeyboardNavigationCluster() - && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) && !hasFocus()) { - // A cluster cannot be focus-entered from outside using forward/backward navigation. - return; - } - final int focusableCount = views.size(); final int descendantFocusability = getDescendantFocusability(); @@ -1175,11 +1166,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void addKeyboardNavigationGroups( - @KeyboardNavigationGroupType int groupType, Collection<View> views, int direction) { + public void addKeyboardNavigationClusters(Collection<View> views, int direction) { final int focusableCount = views.size(); - super.addKeyboardNavigationGroups(groupType, views, direction); + super.addKeyboardNavigationClusters(views, direction); if (focusableCount != views.size()) { // No need to look for groups inside a group. @@ -1195,14 +1185,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; - if (groupType == KEYBOARD_NAVIGATION_GROUP_SECTION - && child.isKeyboardNavigationCluster()) { - // When the current cluster is the default cluster, and we are searching for - // sections, ignore sections inside non-default clusters. - continue; - } if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addKeyboardNavigationGroups(groupType, views, direction); + child.addKeyboardNavigationClusters(views, direction); } } } @@ -3072,8 +3056,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View[] children = mChildren; for (int i = index; i != end; i += increment) { View child = children[i]; - if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE - && !child.isKeyboardNavigationCluster()) { + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { if (child.requestFocus(direction, previouslyFocusedRect)) { return true; } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index c9277ca0bfa1..79b05cdb6e50 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,7 +18,6 @@ package android.view; import android.graphics.Rect; import android.os.Bundle; -import android.view.View.KeyboardNavigationGroupType; import android.view.accessibility.AccessibilityEvent; /** @@ -148,20 +147,17 @@ public interface ViewParent { public View focusSearch(View v, int direction); /** - * Find the nearest keyboard navigation group in the specified direction. The group type can be - * either a cluster or a section. - * This does not actually give focus to that group. + * Find the nearest keyboard navigation cluster in the specified direction. + * This does not actually give focus to that cluster. * - * @param groupType Type of the keyboard navigation group - * @param currentGroup The starting point of the search. Null means the current group is not - * found yet + * @param currentCluster The starting point of the search. Null means the current cluster is not + * found yet * @param direction Direction to look * - * @return The nearest keyboard navigation group in the specified direction, or null if none + * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - View keyboardNavigationGroupSearch( - @KeyboardNavigationGroupType int groupType, View currentGroup, int direction); + View keyboardNavigationClusterSearch(View currentCluster, int direction); /** * Change the z order of the child so it's on top of all other children. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c0f2c377ee88..3cbe82e8b6b9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,8 +16,6 @@ package android.view; -import static android.view.View.KEYBOARD_NAVIGATION_GROUP_CLUSTER; -import static android.view.View.KEYBOARD_NAVIGATION_GROUP_SECTION; import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; @@ -73,7 +71,6 @@ import android.util.TimeUtils; import android.util.TypedValue; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; -import android.view.View.KeyboardNavigationGroupType; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -4397,14 +4394,13 @@ public final class ViewRootImpl implements ViewParent, return false; } - private boolean performKeyboardGroupNavigation( - @KeyboardNavigationGroupType int groupType, int direction) { + private boolean performKeyboardGroupNavigation(int direction) { final View focused = mView.findFocus(); - final View group = focused != null - ? focused.keyboardNavigationGroupSearch(groupType, null, direction) - : keyboardNavigationGroupSearch(groupType, null, direction); + final View cluster = focused != null + ? focused.keyboardNavigationClusterSearch(null, direction) + : keyboardNavigationClusterSearch(null, direction); - if (group != null && group.restoreDefaultFocus(View.FOCUS_DOWN)) { + if (cluster != null && cluster.restoreDefaultFocus(View.FOCUS_DOWN)) { return true; } @@ -4424,32 +4420,15 @@ public final class ViewRootImpl implements ViewParent, } int groupNavigationDirection = 0; - @KeyboardNavigationGroupType int groupType = 0; if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) { final int character = event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK); if (character == '+') { - groupType = KEYBOARD_NAVIGATION_GROUP_CLUSTER; groupNavigationDirection = View.FOCUS_FORWARD; } if (character == '_') { - groupType = KEYBOARD_NAVIGATION_GROUP_CLUSTER; - groupNavigationDirection = View.FOCUS_BACKWARD; - } - } - - if (event.getAction() == KeyEvent.ACTION_DOWN && event.isAltPressed()) { - final int character = - event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_ALT_MASK); - if (character == '+') { - groupType = KEYBOARD_NAVIGATION_GROUP_SECTION; - groupNavigationDirection = View.FOCUS_FORWARD; - } - - if (character == '_') { - groupType = KEYBOARD_NAVIGATION_GROUP_SECTION; groupNavigationDirection = View.FOCUS_BACKWARD; } } @@ -4479,7 +4458,7 @@ public final class ViewRootImpl implements ViewParent, // Handle automatic focus changes. if (event.getAction() == KeyEvent.ACTION_DOWN) { if (groupNavigationDirection != 0) { - if (performKeyboardGroupNavigation(groupType, groupNavigationDirection)) { + if (performKeyboardGroupNavigation(groupNavigationDirection)) { return FINISH_HANDLED; } } else { @@ -5910,11 +5889,10 @@ public final class ViewRootImpl implements ViewParent, * {@inheritDoc} */ @Override - public View keyboardNavigationGroupSearch( - @KeyboardNavigationGroupType int groupType, View currentGroup, int direction) { + public View keyboardNavigationClusterSearch(View currentCluster, int direction) { checkThread(); - return FocusFinder.getInstance().findNextKeyboardNavigationGroup(groupType, - mView, currentGroup, direction); + return FocusFinder.getInstance().findNextKeyboardNavigationCluster( + mView, currentCluster, direction); } public void debug() { diff --git a/core/java/android/view/textclassifier/EntityConfidence.java b/core/java/android/view/textclassifier/EntityConfidence.java new file mode 100644 index 000000000000..7aab71fac76b --- /dev/null +++ b/core/java/android/view/textclassifier/EntityConfidence.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.FloatRange; +import android.annotation.NonNull; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Helper object for setting and getting entity scores for classified text. + * + * @param <T> the entity type. + * @hide + */ +final class EntityConfidence<T> { + + private final Map<T, Float> mEntityConfidence = new HashMap<>(); + + private final Comparator<T> mEntityComparator = (e1, e2) -> { + float score1 = mEntityConfidence.get(e1); + float score2 = mEntityConfidence.get(e2); + if (score1 > score2) { + return 1; + } + if (score1 < score2) { + return -1; + } + return 0; + }; + + EntityConfidence() {} + + EntityConfidence(@NonNull EntityConfidence<T> source) { + Preconditions.checkNotNull(source); + mEntityConfidence.putAll(source.mEntityConfidence); + } + + /** + * Sets an entity type for the classified text and assigns a confidence score. + * + * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence). + * 0 implies the entity does not exist for the classified text. + * Values greater than 1 are clamped to 1. + */ + public void setEntityType( + @NonNull T type, @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { + Preconditions.checkNotNull(type); + if (confidenceScore > 0) { + mEntityConfidence.put(type, Math.min(1, confidenceScore)); + } else { + mEntityConfidence.remove(type); + } + } + + /** + * Returns an immutable list of entities found in the classified text ordered from + * high confidence to low confidence. + */ + @NonNull + public List<T> getEntities() { + List<T> entities = new ArrayList<>(mEntityConfidence.size()); + entities.addAll(mEntityConfidence.keySet()); + entities.sort(mEntityComparator); + return Collections.unmodifiableList(entities); + } + + /** + * Returns the confidence score for the specified entity. The value ranges from + * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the + * classified text. + */ + @FloatRange(from = 0.0, to = 1.0) + public float getConfidenceScore(T entity) { + if (mEntityConfidence.containsKey(entity)) { + return mEntityConfidence.get(entity); + } + return 0; + } + + @Override + public String toString() { + return mEntityConfidence.toString(); + } +} diff --git a/core/java/android/view/textclassifier/LinksInfo.java b/core/java/android/view/textclassifier/LinksInfo.java new file mode 100644 index 000000000000..3acbdc0261bd --- /dev/null +++ b/core/java/android/view/textclassifier/LinksInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.NonNull; + +/** + * Link information that can be applied to text. See: {@link #apply(CharSequence)}. + * Typical implementations of this interface will annotate spannable text with e.g + * {@link android.text.style.ClickableSpan}s or other annotations. + */ +public interface LinksInfo { + + /** + * @hide + */ + LinksInfo NO_OP = text -> false; + + /** + * Applies link annotations to the specified text. + * These annotations are not guaranteed to be applied. For example, the annotations may not be + * applied if the text has changed from what it was when the link spec was generated for it. + * + * @return Whether or not the link annotations were successfully applied. + */ + boolean apply(@NonNull CharSequence text); +} diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java new file mode 100644 index 000000000000..b5ab4bfb1d0a --- /dev/null +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.NonNull; +import android.content.Context; + +import java.util.Collections; +import java.util.List; + +/** + * Interface to the text classification service. + * + * <p>You do not instantiate this class directly; instead, retrieve it through + * {@link android.content.Context#getSystemService}. + */ +public final class TextClassificationManager { + + /** @hide */ + public TextClassificationManager(Context context) {} + + /** + * Returns the default text classifier. + */ + public TextClassifier getDefaultTextClassifier() { + return TextClassifier.NO_OP; + } + + /** + * Returns information containing languages that were detected in the provided text. + * This is a blocking operation you should avoid calling it on the UI thread. + * + * @throws IllegalArgumentException if text is null + */ + public List<TextLanguage> detectLanguages(@NonNull CharSequence text) { + // TODO: Implement + return Collections.emptyList(); + } +} diff --git a/core/java/android/view/textclassifier/TextClassificationResult.java b/core/java/android/view/textclassifier/TextClassificationResult.java new file mode 100644 index 000000000000..6af0efb5fd08 --- /dev/null +++ b/core/java/android/view/textclassifier/TextClassificationResult.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.View.OnClickListener; +import android.view.textclassifier.TextClassifier.EntityType; + +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Information for generating a widget to handle classified text. + */ +public final class TextClassificationResult { + + /** + * @hide + */ + static final TextClassificationResult EMPTY = new TextClassificationResult.Builder().build(); + + @NonNull private final String mText; + @Nullable private final Drawable mIcon; + @Nullable private final String mLabel; + @Nullable private final Intent mIntent; + @Nullable private final OnClickListener mOnClickListener; + @NonNull private final EntityConfidence<String> mEntityConfidence; + @NonNull private final List<String> mEntities; + + private TextClassificationResult( + @NonNull String text, + Drawable icon, + String label, + Intent intent, + OnClickListener onClickListener, + @NonNull EntityConfidence<String> entityConfidence) { + mText = text; + mIcon = icon; + mLabel = label; + mIntent = intent; + mOnClickListener = onClickListener; + mEntityConfidence = new EntityConfidence<>(entityConfidence); + mEntities = mEntityConfidence.getEntities(); + } + + /** + * Gets the classified text. + */ + @NonNull + public String getText() { + return mText; + } + + /** + * Returns the number of entities found in the classified text. + */ + @IntRange(from = 0) + public int getEntityCount() { + return mEntities.size(); + } + + /** + * Returns the entity at the specified index. Entities are ordered from high confidence + * to low confidence. + * + * @throws IndexOutOfBoundsException if the specified index is out of range. + * @see #getEntityCount() for the number of entities available. + */ + @NonNull + public @EntityType String getEntity(int index) { + return mEntities.get(index); + } + + /** + * Returns the confidence score for the specified entity. The value ranges from + * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the + * classified text. + */ + @FloatRange(from = 0.0, to = 1.0) + public float getConfidenceScore(@EntityType String entity) { + return mEntityConfidence.getConfidenceScore(entity); + } + + /** + * Returns an icon that may be rendered on a widget used to act on the classified text. + */ + @Nullable + public Drawable getIcon() { + return mIcon; + } + + /** + * Returns a label that may be rendered on a widget used to act on the classified text. + */ + @Nullable + public CharSequence getLabel() { + return mLabel; + } + + /** + * Returns an intent that may be fired to act on the classified text. + */ + @Nullable + public Intent getIntent() { + return mIntent; + } + + /** + * Returns an OnClickListener that may be triggered to act on the classified text. + */ + @Nullable + public OnClickListener getOnClickListener() { + return mOnClickListener; + } + + @Override + public String toString() { + return String.format("TextClassificationResult {" + + "text=%s, entities=%s, label=%s, intent=%s}", + mText, mEntityConfidence, mLabel, mIntent); + } + + /** + * Creates an OnClickListener that starts an activity with the specified intent. + * + * @throws IllegalArgumentException if context or intent is null + * @hide + */ + @NonNull + public static OnClickListener createStartActivityOnClick( + @NonNull final Context context, @NonNull final Intent intent) { + Preconditions.checkArgument(context != null); + Preconditions.checkArgument(intent != null); + return v -> context.startActivity(intent); + } + + /** + * Builder for building {@link TextClassificationResult}s. + */ + public static final class Builder { + + @NonNull private String mText; + @Nullable private Drawable mIcon; + @Nullable private String mLabel; + @Nullable private Intent mIntent; + @Nullable private OnClickListener mOnClickListener; + @NonNull private final EntityConfidence<String> mEntityConfidence = + new EntityConfidence<>(); + + /** + * Sets the classified text. + */ + public Builder setText(@NonNull String text) { + mText = Preconditions.checkNotNull(text); + return this; + } + + /** + * Sets an entity type for the classification result and assigns a confidence score. + * + * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence). + * 0 implies the entity does not exist for the classified text. + * Values greater than 1 are clamped to 1. + */ + public Builder setEntityType( + @NonNull @EntityType String type, + @FloatRange(from = 0.0, to = 1.0)float confidenceScore) { + mEntityConfidence.setEntityType(type, confidenceScore); + return this; + } + + /** + * Sets an icon that may be rendered on a widget used to act on the classified text. + */ + public Builder setIcon(@Nullable Drawable icon) { + mIcon = icon; + return this; + } + + /** + * Sets a label that may be rendered on a widget used to act on the classified text. + */ + public Builder setLabel(@Nullable String label) { + mLabel = label; + return this; + } + + /** + * Sets an intent that may be fired to act on the classified text. + */ + public Builder setIntent(@Nullable Intent intent) { + mIntent = intent; + return this; + } + + /** + * Sets an OnClickListener that may be triggered to act on the classified text. + */ + public Builder setOnClickListener(@Nullable OnClickListener onClickListener) { + mOnClickListener = onClickListener; + return this; + } + + /** + * Builds an returns a {@link TextClassificationResult}. + */ + public TextClassificationResult build() { + return new TextClassificationResult( + mText, mIcon, mLabel, mIntent, mOnClickListener, mEntityConfidence); + } + } +} diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java new file mode 100644 index 000000000000..b84e2ae5e4fd --- /dev/null +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Interface for providing text classification related features. + * + * <p>Unless otherwise stated, methods of this interface are blocking operations and you should + * avoid calling them on the UI thread. + */ +public interface TextClassifier { + + String TYPE_OTHER = "other"; + String TYPE_EMAIL = "email"; + String TYPE_PHONE = "phone"; + String TYPE_ADDRESS = "address"; + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + TYPE_OTHER, TYPE_EMAIL, TYPE_PHONE, TYPE_ADDRESS + }) + @interface EntityType {} + + /** + * No-op TextClassifier. + * This may be used to turn off TextClassifier features. + */ + TextClassifier NO_OP = new TextClassifier() { + + @Override + public TextSelection suggestSelection( + CharSequence text, int selectionStartIndex, int selectionEndIndex) { + return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build(); + } + + @Override + public TextClassificationResult getTextClassificationResult( + CharSequence text, int startIndex, int endIndex) { + return TextClassificationResult.EMPTY; + } + + @Override + public LinksInfo getLinks(CharSequence text, int linkMask) { + return LinksInfo.NO_OP; + } + }; + + /** + * Returns suggested text selection indices, recognized types and their associated confidence + * scores. The selections are ordered from highest to lowest scoring. + * + * @param text text providing context for the selected text (which is specified + * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex) + * @param selectionStartIndex start index of the selected part of text + * @param selectionEndIndex end index of the selected part of text + * + * @throws IllegalArgumentException if text is null; selectionStartIndex is negative; + * selectionEndIndex is greater than text.length() or less than selectionStartIndex + */ + @NonNull + TextSelection suggestSelection( + @NonNull CharSequence text, + @IntRange(from = 0) int selectionStartIndex, + @IntRange(from = 0) int selectionEndIndex); + + /** + * Returns a {@link TextClassificationResult} object that can be used to generate a widget for + * handling the classified text. + * + * @param text text providing context for the text to classify (which is specified + * by the sub sequence starting at startIndex and ending at endIndex) + * @param startIndex start index of the text to classify + * @param endIndex end index of the text to classify + * + * @throws IllegalArgumentException if text is null; startIndex is negative; + * endIndex is greater than text.length() or less than startIndex + */ + @NonNull + TextClassificationResult getTextClassificationResult( + @NonNull CharSequence text, int startIndex, int endIndex); + + /** + * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links + * information. + * + * @param text the text to generate annotations for + * @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be + * specified. Subclasses of this interface may specify additional linkMasks + * + * @throws IllegalArgumentException if text is null + */ + LinksInfo getLinks(@NonNull CharSequence text, int linkMask); +} diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java new file mode 100644 index 000000000000..d94d163577e1 --- /dev/null +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.Preconditions; + +import java.util.List; +import java.util.Locale; + +/** + * Specifies detected languages for a section of text indicated by a start and end index. + */ +public final class TextLanguage { + + private final int mStartIndex; + private final int mEndIndex; + @NonNull private final EntityConfidence<Locale> mLanguageConfidence; + @NonNull private final List<Locale> mLanguages; + + private TextLanguage( + int startIndex, int endIndex, @NonNull EntityConfidence<Locale> languageConfidence) { + mStartIndex = startIndex; + mEndIndex = endIndex; + mLanguageConfidence = new EntityConfidence<>(languageConfidence); + mLanguages = mLanguageConfidence.getEntities(); + } + + /** + * Returns the start index of the detected languages in the text provided to generate this + * object. + */ + public int getStartIndex() { + return mStartIndex; + } + + /** + * Returns the end index of the detected languages in the text provided to generate this object. + */ + public int getEndIndex() { + return mEndIndex; + } + + /** + * Returns the number of languages found in the classified text. + */ + @IntRange(from = 0) + public int getLanguageCount() { + return mLanguages.size(); + } + + /** + * Returns the language locale at the specified index. + * Language locales are ordered from high confidence to low confidence. + * + * @throws IndexOutOfBoundsException if the specified index is out of range. + * @see #getLanguageCount() for the number of language locales available. + */ + @NonNull + public Locale getLanguage(int index) { + return mLanguages.get(index); + } + + /** + * Returns the confidence score for the specified language. The value ranges from + * 0 (low confidence) to 1 (high confidence). 0 indicates that the language was + * not found for the classified text. + */ + @FloatRange(from = 0.0, to = 1.0) + public float getConfidenceScore(@Nullable Locale language) { + return mLanguageConfidence.getConfidenceScore(language); + } + + @Override + public String toString() { + return String.format("TextLanguage {%d, %d, %s}", + mStartIndex, mEndIndex, mLanguageConfidence); + } + + /** + * Builder to build {@link TextLanguage} objects. + */ + public static final class Builder { + + private final int mStartIndex; + private final int mEndIndex; + @NonNull private final EntityConfidence<Locale> mLanguageConfidence = + new EntityConfidence<>(); + + /** + * Creates a builder to build {@link TextLanguage} objects. + * + * @param startIndex the start index of the detected languages in the text provided + * to generate the result + * @param endIndex the end index of the detected languages in the text provided + * to generate the result. Must be greater than startIndex + */ + public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) { + Preconditions.checkArgument(startIndex >= 0); + Preconditions.checkArgument(endIndex > startIndex); + mStartIndex = startIndex; + mEndIndex = endIndex; + } + + /** + * Sets a language locale with the associated confidence score. + */ + public Builder setLanguage( + @NonNull Locale locale, @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { + mLanguageConfidence.setEntityType(locale, confidenceScore); + return this; + } + + /** + * Builds and returns a {@link TextLanguage}. + */ + public TextLanguage build() { + return new TextLanguage(mStartIndex, mEndIndex, mLanguageConfidence); + } + } +} diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java new file mode 100644 index 000000000000..3172c13daa80 --- /dev/null +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2017 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.textclassifier; + +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.view.textclassifier.TextClassifier.EntityType; + +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Information about where text selection should be. + */ +public final class TextSelection { + + private final int mStartIndex; + private final int mEndIndex; + @NonNull private final EntityConfidence<String> mEntityConfidence; + @NonNull private final List<String> mEntities; + + private TextSelection( + int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) { + mStartIndex = startIndex; + mEndIndex = endIndex; + mEntityConfidence = new EntityConfidence<>(entityConfidence); + mEntities = mEntityConfidence.getEntities(); + } + + /** + * Returns the start index of the text selection. + */ + public int getSelectionStartIndex() { + return mStartIndex; + } + + /** + * Returns the end index of the text selection. + */ + public int getSelectionEndIndex() { + return mEndIndex; + } + + /** + * Returns the number of entities found in the classified text. + */ + @IntRange(from = 0) + public int getEntityCount() { + return mEntities.size(); + } + + /** + * Returns the entity at the specified index. Entities are ordered from high confidence + * to low confidence. + * + * @throws IndexOutOfBoundsException if the specified index is out of range. + * @see #getEntityCount() for the number of entities available. + */ + @NonNull + public @EntityType String getEntity(int index) { + return mEntities.get(index); + } + + /** + * Returns the confidence score for the specified entity. The value ranges from + * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the + * classified text. + */ + @FloatRange(from = 0.0, to = 1.0) + public float getConfidenceScore(@EntityType String entity) { + return mEntityConfidence.getConfidenceScore(entity); + } + + @Override + public String toString() { + return String.format("TextSelection {%d, %d, %s}", + mStartIndex, mEndIndex, mEntityConfidence); + } + + /** + * Builder used to build {@link TextSelection} objects. + */ + public static final class Builder { + + private final int mStartIndex; + private final int mEndIndex; + @NonNull private final EntityConfidence<String> mEntityConfidence = + new EntityConfidence<>(); + + /** + * Creates a builder used to build {@link TextSelection} objects. + * + * @param startIndex the start index of the text selection. + * @param endIndex the end index of the text selection. Must be greater than startIndex + */ + public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) { + Preconditions.checkArgument(startIndex >= 0); + Preconditions.checkArgument(endIndex > startIndex); + mStartIndex = startIndex; + mEndIndex = endIndex; + } + + /** + * Sets an entity type for the classified text and assigns a confidence score. + * + * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence). + * 0 implies the entity does not exist for the classified text. + * Values greater than 1 are clamped to 1. + */ + public Builder setEntityType( + @NonNull @EntityType String type, + @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { + mEntityConfidence.setEntityType(type, confidenceScore); + return this; + } + + /** + * Builds and returns {@link TextSelection} object. + */ + public TextSelection build() { + return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence); + } + } +} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 2fc8ec9c41f2..f7f9a81e45c1 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -60,8 +60,6 @@ import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.StaticLayout; -import android.text.TextClassification; -import android.text.TextSelection; import android.text.TextUtils; import android.text.method.KeyListener; import android.text.method.MetaKeyKeyListener; @@ -108,6 +106,8 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.view.textclassifier.TextClassificationResult; +import android.view.textclassifier.TextSelection; import android.widget.AdapterView.OnItemClickListener; import android.widget.TextView.Drawables; import android.widget.TextView.OnEditorActionListener; @@ -149,7 +149,7 @@ public class Editor { private static final String UNDO_OWNER_TAG = "Editor"; // Ordering constants used to place the Action Mode or context menu items in their menu. - // Reserve 1 for the app that the ASSIST logic suggests as the best app to handle the selection. + private static final int MENU_ITEM_ORDER_ASSIST = 1; private static final int MENU_ITEM_ORDER_UNDO = 2; private static final int MENU_ITEM_ORDER_REDO = 3; private static final int MENU_ITEM_ORDER_SHARE = 4; @@ -238,6 +238,8 @@ public class Editor { private boolean mPreserveSelection; private boolean mRestartActionModeOnNextRefresh; + private TextClassificationResult mTextClassificationResult; + boolean mIsBeingLongClicked; private SuggestionsPopupWindow mSuggestionsPopupWindow; @@ -1889,7 +1891,7 @@ public class Editor { mInsertionPointCursorController.invalidateHandle(); } if (mTextActionMode != null) { - mTextActionMode.invalidate(); + invalidateActionMode(getTextClassifierInfo(false)); } } @@ -1982,12 +1984,12 @@ public class Editor { if (mRestartActionModeOnNextRefresh) { // To avoid distraction, newly start action mode only when selection action // mode is being restarted. - startSelectionActionMode(); + startSelectionActionMode(getTextClassifierInfo(true)); } } else if (selectionController == null || !selectionController.isActive()) { // Insertion action mode is active. Avoid dismissing the selection. stopTextActionModeWithPreservingSelection(); - startSelectionActionMode(); + startSelectionActionMode(getTextClassifierInfo(true)); } else { mTextActionMode.invalidateContentRect(); } @@ -2031,7 +2033,8 @@ public class Editor { * * @return true if the selection mode was actually started. */ - boolean startSelectionActionMode() { + boolean startSelectionActionMode(@Nullable TextClassificationResult textClassificationResult) { + mTextClassificationResult = textClassificationResult; boolean selectionStarted = startSelectionActionModeInternal(); if (selectionStarted) { getSelectionController().show(); @@ -2040,6 +2043,40 @@ public class Editor { return selectionStarted; } + private boolean startSelectionActionModeWithTextAssistant() { + return startSelectionActionMode(getTextClassifierInfo(true)); + } + + private void invalidateActionMode(TextClassificationResult textClassificationResult) { + mTextClassificationResult = textClassificationResult; + mTextActionMode.invalidate(); + } + + // TODO: Make this a non-blocking call. + private TextClassificationResult getTextClassifierInfo(boolean updateSelection) { + // TODO: Trim the text so that only text necessary to provide context of the selected + // text is sent to the assistant. + final int trimStartIndex = 0; + final int trimEndIndex = mTextView.getText().length(); + CharSequence trimmedText = + mTextView.getText().subSequence(trimStartIndex, trimEndIndex); + int startIndex = mTextView.getSelectionStart() - trimStartIndex; + int endIndex = mTextView.getSelectionEnd() - trimStartIndex; + + if (updateSelection) { + TextSelection textSelection = mTextView.getTextClassifier() + .suggestSelection(trimmedText, startIndex, endIndex); + startIndex = Math.max(0, textSelection.getSelectionStartIndex() + trimStartIndex); + endIndex = Math.min(mTextView.getText().length(), + textSelection.getSelectionEndIndex() + trimStartIndex); + Selection.setSelection((Spannable) mTextView.getText(), startIndex, endIndex); + return getTextClassifierInfo(false); + } + + return mTextView.getTextClassifier() + .getTextClassificationResult(trimmedText, startIndex, endIndex); + } + /** * If the TextView allows text selection, selects the current word when no existing selection * was available and starts a drag. @@ -2086,7 +2123,7 @@ public class Editor { } if (mTextActionMode != null) { // Text action mode is already started - mTextActionMode.invalidate(); + invalidateActionMode(getTextClassifierInfo(false)); return false; } @@ -3744,8 +3781,7 @@ public class Editor { private final Path mSelectionPath = new Path(); private final RectF mSelectionBounds = new RectF(); private final boolean mHasSelection; - - private int mHandleHeight; + private final int mHandleHeight; public TextActionModeCallback(boolean hasSelection) { mHasSelection = hasSelection; @@ -3765,18 +3801,19 @@ public class Editor { if (insertionController != null) { insertionController.getHandle(); mHandleHeight = mSelectHandleCenter.getMinimumHeight(); + } else { + mHandleHeight = 0; } } } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - TextClassification textClassification = updateSelectionWithTextAssistant(); - mode.setTitle(null); mode.setSubtitle(null); mode.setTitleOptionalHint(true); - populateMenuWithItems(menu, textClassification); + populateMenuWithItems(menu); + updateAssistMenuItem(menu, mTextClassificationResult); Callback customCallback = getCustomCallback(); if (customCallback != null) { @@ -3802,30 +3839,13 @@ public class Editor { } } - private TextClassification updateSelectionWithTextAssistant() { - // Trim the text so that only text necessary to provide context of the selected text is - // sent to the assistant. - CharSequence trimmedText = mTextView.getText(); - int textLength = mTextView.getText().length(); - int trimStartIndex = 0; - int startIndex = mTextView.getSelectionStart() - trimStartIndex; - int endIndex = mTextView.getSelectionEnd() - trimStartIndex; - TextSelection textSelection = mTextView.getTextAssistant() - .suggestSelection(trimmedText, startIndex, endIndex); - Selection.setSelection( - (Spannable) mTextView.getText(), - Math.max(0, textSelection.getSelectionStartIndex() + trimStartIndex), - Math.min(textLength, textSelection.getSelectionEndIndex() + trimStartIndex)); - return textSelection.getTextClassification(); - } - private Callback getCustomCallback() { return mHasSelection ? mCustomSelectionActionModeCallback : mCustomInsertionActionModeCallback; } - private void populateMenuWithItems(Menu menu, TextClassification textClassification) { + private void populateMenuWithItems(Menu menu) { if (mTextView.canCut()) { menu.add(Menu.NONE, TextView.ID_CUT, MENU_ITEM_ORDER_CUT, com.android.internal.R.string.cut) @@ -3855,13 +3875,13 @@ public class Editor { updateSelectAllItem(menu); updateReplaceItem(menu); - updateAssistMenuItem(menu, textClassification); } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateSelectAllItem(menu); updateReplaceItem(menu); + updateAssistMenuItem(menu, mTextClassificationResult); Callback customCallback = getCustomCallback(); if (customCallback != null) { @@ -3894,10 +3914,16 @@ public class Editor { } } - private void updateAssistMenuItem(Menu menu, TextClassification textClassification) { - // TODO: Find the best app available to handle the selected text based on information in - // the TextClassification object. - // Add app icon + intent to trigger app to the menu. + private void updateAssistMenuItem( + Menu menu, TextClassificationResult textClassificationResult) { + menu.removeItem(TextView.ID_ASSIST); + if (textClassificationResult != null + && textClassificationResult.getIcon() != null + && textClassificationResult.getOnClickListener() != null) { + menu.add(Menu.NONE, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, null) + .setIcon(textClassificationResult.getIcon()) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + } } @Override @@ -3909,6 +3935,10 @@ public class Editor { if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { return true; } + if (TextView.ID_ASSIST == item.getItemId() && mTextClassificationResult != null) { + mTextClassificationResult.getOnClickListener().onClick(mTextView); + stopTextActionMode(); + } return mTextView.onTextContextMenuItem(item.getItemId()); } @@ -3916,6 +3946,7 @@ public class Editor { public void onDestroyActionMode(ActionMode mode) { // Clear mTextActionMode not to recursively destroy action mode by clearing selection. mTextActionMode = null; + mTextClassificationResult = null; Callback customCallback = getCustomCallback(); if (customCallback != null) { customCallback.onDestroyActionMode(mode); @@ -4733,7 +4764,7 @@ public class Editor { } positionAtCursorOffset(offset, false); if (mTextActionMode != null) { - mTextActionMode.invalidate(); + invalidateActionMode(getTextClassifierInfo(false)); } } @@ -4817,7 +4848,7 @@ public class Editor { } updateDrawable(); if (mTextActionMode != null) { - mTextActionMode.invalidate(); + invalidateActionMode(getTextClassifierInfo(false)); } } @@ -5465,7 +5496,8 @@ public class Editor { resetDragAcceleratorState(); if (mTextView.hasSelection()) { - startSelectionActionMode(); + // TODO: Do not invoke the text assistant if this was a drag selection. + startSelectionActionMode(getTextClassifierInfo(true)); } break; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b1fc67e56d80..2f303cdba301 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -77,8 +77,6 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; import android.text.StaticLayout; -import android.text.TextAssistant; -import android.text.TextClassificationManager; import android.text.TextDirectionHeuristic; import android.text.TextDirectionHeuristics; import android.text.TextPaint; @@ -146,6 +144,8 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.view.textclassifier.TextClassificationManager; +import android.view.textclassifier.TextClassifier; import android.view.textservice.SpellCheckerSubtype; import android.view.textservice.TextServicesManager; import android.widget.RemoteViews.RemoteView; @@ -352,6 +352,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mPreDrawRegistered; private boolean mPreDrawListenerDetached; + private TextClassifier mTextClassifier; + // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is // that if a user is holding down a movement key to traverse text, we shouldn't also traverse // the view hierarchy. On the other hand, if the user is using the movement key to traverse @@ -9890,7 +9892,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) text, start, end); // Make sure selection mode is engaged. if (mEditor != null) { - mEditor.startSelectionActionMode(); + mEditor.startSelectionActionMode(null); } return true; } @@ -10034,6 +10036,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener static final int ID_SHARE = android.R.id.shareText; static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText; static final int ID_REPLACE = android.R.id.replaceText; + static final int ID_ASSIST = android.R.id.textAssist; /** * Called when a context menu option for the text view is selected. Currently @@ -10258,33 +10261,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mEditor == null ? null : mEditor.mCustomInsertionActionModeCallback; } - private TextAssistant mTextAssistant; - /** - * Sets the {@link TextAssistant} for this TextView. - * If null, this TextView uses the default TextAssistant which comes from the Activity. + * Sets the {@link TextClassifier} for this TextView. */ - public void setTextAssistant(TextAssistant textAssistant) { - mTextAssistant = textAssistant; + public void setTextClassifier(@Nullable TextClassifier textClassifier) { + mTextClassifier = textClassifier; } /** - * Returns the {@link TextAssistant} used by this TextView. - * If no TextAssistant is set, it'll use the one from this TextView's {@link Activity} or - * {@link Context}. If no TextAssistant is found, it'll use a no-op TextAssistant. + * Returns the {@link TextClassifier} used by this TextView. + * If no TextClassifier has been set, this TextView uses the default set by the + * {@link TextClassificationManager}. */ - public TextAssistant getTextAssistant() { - if (mTextAssistant != null) { - return mTextAssistant; - } - if (mContext instanceof Activity) { - mTextAssistant = ((Activity) mContext).getTextAssistant(); - } else { - // The context of this TextView should be an Activity. If it is not and no - // text assistant has been set, return the TextClassificationManager. - mTextAssistant = mContext.getSystemService(TextClassificationManager.class); + @NonNull + public TextClassifier getTextClassifier() { + if (mTextClassifier == null) { + TextClassificationManager tcm = + mContext.getSystemService(TextClassificationManager.class); + if (tcm != null) { + mTextClassifier = tcm.getDefaultTextClassifier(); + } else { + mTextClassifier = TextClassifier.NO_OP; + } } - return mTextAssistant; + return mTextClassifier; } /** diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index 8d117837df83..a94b1612e8a3 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java @@ -95,39 +95,32 @@ public class InputMethodSubtypeSwitchingController { } } + private static int compareNullableCharSequences(@Nullable CharSequence c1, + @Nullable CharSequence c2) { + // For historical reasons, an empty text needs to put at the last. + final boolean empty1 = TextUtils.isEmpty(c1); + final boolean empty2 = TextUtils.isEmpty(c2); + if (empty1 || empty2) { + return (empty1 ? 1 : 0) - (empty2 ? 1 : 0); + } + return c1.toString().compareTo(c2.toString()); + } + @Override public int compareTo(ImeSubtypeListItem other) { - if (TextUtils.isEmpty(mImeName)) { - return 1; - } - if (TextUtils.isEmpty(other.mImeName)) { - return -1; - } - if (!TextUtils.equals(mImeName, other.mImeName)) { - return mImeName.toString().compareTo(other.mImeName.toString()); - } - if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) { - return 0; - } - if (mIsSystemLocale) { - return -1; - } - if (other.mIsSystemLocale) { - return 1; - } - if (mIsSystemLanguage) { - return -1; - } - if (other.mIsSystemLanguage) { - return 1; + int result = compareNullableCharSequences(mImeName, other.mImeName); + if (result != 0) { + return result; } - if (TextUtils.isEmpty(mSubtypeName)) { - return 1; + result = compareNullableCharSequences(mSubtypeName, other.mSubtypeName); + if (result != 0) { + return result; } - if (TextUtils.isEmpty(other.mSubtypeName)) { - return -1; + result = (mIsSystemLocale ? -1 : 0) - (other.mIsSystemLocale ? -1 : 0); + if (result != 0) { + return result; } - return mSubtypeName.toString().compareTo(other.mSubtypeName.toString()); + return (mIsSystemLanguage ? -1 : 0) - (other.mIsSystemLanguage ? -1 : 0); } @Override diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 716997f815dc..c08cd7284876 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -1080,22 +1080,6 @@ public class InputMethodUtils { return enabledSubtypes; } - // At the initial boot, the settings for input methods are not set, - // so we need to enable IME in that case. - public void enableAllIMEsIfThereIsNoEnabledIME() { - if (TextUtils.isEmpty(getEnabledInputMethodsStr())) { - StringBuilder sb = new StringBuilder(); - final int N = mMethodList.size(); - for (int i = 0; i < N; i++) { - InputMethodInfo imi = mMethodList.get(i); - Slog.i(TAG, "Adding: " + imi.getId()); - if (i > 0) sb.append(':'); - sb.append(imi.getId()); - } - putEnabledInputMethodsStr(sb.toString()); - } - } - public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() { return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(), mInputMethodSplitter, diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java index 7eda3da70bba..2d78979b4c03 100644 --- a/core/java/com/android/internal/logging/LogBuilder.java +++ b/core/java/com/android/internal/logging/LogBuilder.java @@ -23,6 +23,7 @@ import android.view.View; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + /** * Helper class to assemble more complex logs. * @@ -31,6 +32,13 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; public class LogBuilder { private static final String TAG = "LogBuilder"; + + // Min required eventlog line length. + // See: android/util/cts/EventLogTest.java + // Size checks enforced here are intended only as sanity checks; + // your logs may be truncated earlier. Please log responsibly. + public static final int MAX_SERIALIZED_SIZE = 4000; + private SparseArray<Object> entries = new SparseArray(); public LogBuilder(int mainCategory) { @@ -97,7 +105,11 @@ public class LogBuilder { throw new IllegalArgumentException( "Value must be loggable type - int, long, float, String"); } - entries.put(tag, value); + if (value.toString().getBytes().length > MAX_SERIALIZED_SIZE) { + Log.i(TAG, "Log value too long, omitted: " + value.toString()); + } else { + entries.put(tag, value); + } return this; } @@ -198,18 +210,23 @@ public class LogBuilder { out[i * 2] = entries.keyAt(i); out[i * 2 + 1] = entries.valueAt(i); } + int size = out.toString().getBytes().length; + if (size > MAX_SERIALIZED_SIZE) { + Log.i(TAG, "Log line too long, did not emit: " + size + " bytes."); + throw new RuntimeException(); + } return out; } public void deserialize(Object[] items) { int i = 0; - while(i < items.length) { + while (i < items.length) { Object key = items[i++]; Object value = i < items.length ? items[i++] : null; if (key instanceof Integer) { entries.put((Integer) key, value); } else { - Log.i(TAG, "Invalid key " + key.toString()); + Log.i(TAG, "Invalid key " + key.toString()); } } } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 16c2719c0584..b90336c442aa 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -94,9 +94,6 @@ public class MetricsLogger { } public static void action(LogBuilder content) { - //EventLog.writeEvent(524292, content.serialize()); - // Below would be the *right* way to do this, using the generated - // EventLogTags method, but that doesn't work. if (content.getType() == MetricsEvent.TYPE_UNKNOWN) { content.setType(MetricsEvent.TYPE_ACTION); } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 87c2b255115a..df7a5f523caa 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2896,9 +2896,7 @@ See {@link android.view.View#setKeyboardNavigationCluster(boolean)}. --> <attr name="keyboardNavigationCluster" format="boolean" /> - <!-- Whether this view is a root of a keyboard navigation section. - See {@link android.view.View#setKeyboardNavigationSection(boolean)}. --> - <attr name="keyboardNavigationSection" format="boolean" /> + <attr name="__removed0" format="boolean" /> <!-- Defines the next keyboard navigation cluster. @@ -2907,12 +2905,7 @@ will result when the reference is accessed.--> <attr name="nextClusterForward" format="reference"/> - <!-- Defines the next keyboard navigation section. - - If the reference refers to a view that does not exist or is part - of a hierarchy that is invisible, a {@link java.lang.RuntimeException} - will result when the reference is accessed.--> - <attr name="nextSectionForward" format="reference"/> + <attr name="__removed1" format="reference"/> <!-- Whether this view is a default-focus view. Only one view per keyboard navigation cluster can have this attribute set to true. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 5235116f7a08..dfa672d6782c 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1240,6 +1240,10 @@ <!-- An XML resource with the application's Network Security Config. --> <attr name="networkSecurityConfig" format="reference" /> + <!-- When an application is partitioned into splits, this is the name of the + split that contains the defined component. --> + <attr name="splitName" format="string" /> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -1823,6 +1827,8 @@ <attr name="singleUser" /> <attr name="directBootAware" /> <attr name="visibleToInstantApps" /> + <!-- The code for this component is located in the given split. --> + <attr name="splitName" /> </declare-styleable> <!-- Attributes that can be supplied in an AndroidManifest.xml @@ -1913,6 +1919,8 @@ must also be {@link android.R.attr#exported} if this flag is set. --> <attr name="externalService" format="boolean" /> <attr name="visibleToInstantApps" /> + <!-- The code for this component is located in the given split. --> + <attr name="splitName" /> </declare-styleable> <!-- The <code>receiver</code> tag declares an @@ -2036,6 +2044,8 @@ <attr name="onTopLauncher" format="boolean" /> <attr name="rotationAnimation" /> <attr name="visibleToInstantApps" /> + <!-- The code for this component is located in the given split. --> + <attr name="splitName" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7de48d3305bb..c36279c28fdb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2090,6 +2090,7 @@ <item>com.android.server.notification.ImportanceExtractor</item> <item>com.android.server.notification.NotificationIntrusivenessExtractor</item> <item>com.android.server.notification.VisibilityExtractor</item> + <item>com.android.server.notification.BadgeExtractor</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/ids.xml b/core/res/res/values/ids.xml index f351b7008547..613616fa0ea6 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -97,6 +97,7 @@ <item type="id" name="redo" /> <item type="id" name="replaceText" /> <item type="id" name="shareText" /> + <item type="id" name="textAssist" /> <item type="id" name="selection_start_handle" /> <item type="id" name="selection_end_handle" /> <item type="id" name="insertion_handle" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 060c59ed4a31..30da26b00100 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2777,9 +2777,9 @@ <public name="paddingVertical" /> <public name="visibleToInstantApps" /> <public name="keyboardNavigationCluster" /> - <public name="keyboardNavigationSection" /> + <public name="__removed0" /> <public name="nextClusterForward" /> - <public name="nextSectionForward" /> + <public name="__removed1" /> <public name="textColorError" /> <public name="focusedByDefault" /> <public name="appCategory" /> @@ -2793,6 +2793,7 @@ </public-group> <public-group type="id" first-id="0x01020041"> + <public name="textAssist" /> </public-group> <public type="attr" name="primaryContentAlpha" /> diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java index 34c34d7c07bb..dc7541709dac 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java @@ -33,7 +33,8 @@ import java.util.Arrays; import java.util.List; public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase { - private static final String DUMMY_PACKAGE_NAME = "dymmy package name"; + private static final String DUMMY_PACKAGE_NAME = "dummy package name"; + private static final String DUMMY_IME_LABEL = "dummy ime label"; private static final String DUMMY_SETTING_ACTIVITY_NAME = ""; private static final boolean DUMMY_IS_AUX_IME = false; private static final boolean DUMMY_FORCE_DEFAULT = false; @@ -88,6 +89,35 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe } } + private static ImeSubtypeListItem createDummyItem(String imeName, + String subtypeName, String subtypeLocale, int subtypeIndex, String systemLocale) { + final ResolveInfo ri = new ResolveInfo(); + final ServiceInfo si = new ServiceInfo(); + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = DUMMY_PACKAGE_NAME; + ai.enabled = true; + si.applicationInfo = ai; + si.enabled = true; + si.packageName = DUMMY_PACKAGE_NAME; + si.name = imeName; + si.exported = true; + si.nonLocalizedLabel = DUMMY_IME_LABEL; + ri.serviceInfo = si; + ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); + subtypes.add(new InputMethodSubtypeBuilder() + .setSubtypeNameResId(0) + .setSubtypeIconResId(0) + .setSubtypeLocale(subtypeLocale) + .setIsAsciiCapable(true) + .build()); + final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME, + DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID, + DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */, + false /* supportsDismissingWindow */); + return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale, + systemLocale); + } + private static List<ImeSubtypeListItem> createEnabledImeSubtypes() { final List<ImeSubtypeListItem> items = new ArrayList<>(); addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"), @@ -329,4 +359,56 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe assertFalse(item_e.mIsSystemLocale); assertFalse(item_EN_US.mIsSystemLocale); } + + @SmallTest + public void testImeSubtypeListComparator() throws Exception { + { + final List<ImeSubtypeListItem> items = Arrays.asList( + createDummyItem("X", "A", "en_US", 0, "en_US"), + createDummyItem("X", "A", "en", 1, "en_US"), + createDummyItem("X", "A", "ja", 2, "en_US"), + createDummyItem("X", "Z", "en_US", 3, "en_US"), + createDummyItem("X", "Z", "en", 4, "en_US"), + createDummyItem("X", "Z", "ja", 5, "en_US"), + createDummyItem("X", "", "en_US", 6, "en_US"), + createDummyItem("X", "", "en", 7, "en_US"), + createDummyItem("X", "", "ja", 8, "en_US"), + createDummyItem("Y", "A", "en_US", 9, "en_US"), + createDummyItem("Y", "A", "en", 10, "en_US"), + createDummyItem("Y", "A", "ja", 11, "en_US"), + createDummyItem("Y", "Z", "en_US", 12, "en_US"), + createDummyItem("Y", "Z", "en", 13, "en_US"), + createDummyItem("Y", "Z", "ja", 14, "en_US"), + createDummyItem("Y", "", "en_US", 15, "en_US"), + createDummyItem("Y", "", "en", 16, "en_US"), + createDummyItem("Y", "", "ja", 17, "en_US"), + createDummyItem("", "A", "en_US", 18, "en_US"), + createDummyItem("", "A", "en", 19, "en_US"), + createDummyItem("", "A", "ja", 20, "en_US"), + createDummyItem("", "Z", "en_US", 21, "en_US"), + createDummyItem("", "Z", "en", 22, "en_US"), + createDummyItem("", "Z", "ja", 23, "en_US"), + createDummyItem("", "", "en_US", 24, "en_US"), + createDummyItem("", "", "en", 25, "en_US"), + createDummyItem("", "", "ja", 26, "en_US")); + + for (int i = 0; i < items.size(); ++i) { + assertEquals(0, items.get(i).compareTo(items.get(i))); + for (int j = i + 1; j < items.size(); ++j) { + assertTrue(items.get(i).compareTo(items.get(j)) < 0); + assertTrue(items.get(j).compareTo(items.get(i)) > 0); + } + } + } + + { + // Following two items have the same priority. + final ImeSubtypeListItem nonSystemLocale1 = + createDummyItem("X", "A", "ja_JP", 0, "en_us"); + final ImeSubtypeListItem nonSystemLocale2 = + createDummyItem("X", "A", "hi_IN", 1, "en_us"); + assertEquals(0, nonSystemLocale1.compareTo(nonSystemLocale2)); + assertEquals(0, nonSystemLocale2.compareTo(nonSystemLocale1)); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java index a3405591182d..1c19d88815cd 100644 --- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java +++ b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java @@ -107,11 +107,21 @@ public class LogBuilderTest extends TestCase { assertEquals(123.0F, out[7]); } - public void testCategoryDefault() { + public void testCategoryDefault() { LogBuilder builder = new LogBuilder(10); Object[] out = builder.serialize(); assertEquals(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY, out[0]); assertEquals(10, out[1]); } + public void testGiantLogOmitted() { + LogBuilder badBuilder = new LogBuilder(0); + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 4000; i++) { + b.append("test, " + i); + } + badBuilder.addTaggedData(100, b.toString()); + assertTrue(badBuilder.serialize().length < LogBuilder.MAX_SERIALIZED_SIZE); + } + } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 432e77ccd720..a76a328e2274 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -192,5 +192,7 @@ interface IAudioService { oneway void releasePlayer(in int piid); + void disableRingtoneSync(); + // WARNING: read warning at top of file, it is recommended to add new methods at the end } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 7614999074c6..8a1027b83033 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -33,8 +33,11 @@ import android.database.Cursor; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Environment; +import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.MediaStore; @@ -850,6 +853,18 @@ public class RingtoneManager { public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) { final ContentResolver resolver = context.getContentResolver(); + if (Settings.Secure.getString(resolver, Settings.Secure.SYNC_PARENT_SOUNDS).equals("1")) { + // Sync is enabled, so we need to disable it + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + audioService.disableRingtoneSync(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to disable ringtone sync."); + return; + } + } + String setting = getSettingForType(type); if (setting == null) return; if(!isInternalRingtoneUri(ringtoneUri)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 6ac5cb8d3516..49d2462f13f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -24,6 +24,7 @@ import android.app.ActivityOptions; import android.app.INotificationManager; import android.app.KeyguardManager; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.RemoteInput; @@ -1038,6 +1039,7 @@ public abstract class BaseStatusBar extends SystemUI implements private void bindGuts(final ExpandableNotificationRow row) { row.inflateGuts(); final StatusBarNotification sbn = row.getStatusBarNotification(); + final NotificationChannel channel = row.getEntry().channel; PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier()); row.setTag(sbn.getPackageName()); final NotificationGuts guts = row.getGuts(); @@ -1077,8 +1079,8 @@ public abstract class BaseStatusBar extends SystemUI implements closeControls(row, guts, v); } }; - guts.bindNotification(pmUser, iNotificationManager, sbn, onSettingsClick, onDoneClick, - mNonBlockablePkgs); + guts.bindNotification(pmUser, iNotificationManager, sbn, channel, + onSettingsClick, onDoneClick, mNonBlockablePkgs); } private void closeControls( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 458daf162b24..3a891860c0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.graphics.drawable.Icon; @@ -55,6 +56,7 @@ public class NotificationData { private static final int COLOR_INVALID = 1; public String key; public StatusBarNotification notification; + public NotificationChannel channel; public StatusBarIconView icon; public StatusBarIconView expandedIcon; public ExpandableNotificationRow row; // the outer expanded view @@ -429,6 +431,14 @@ public class NotificationData { return null; } + public NotificationChannel getChannel(String key) { + if (mRankingMap != null) { + mRankingMap.getRanking(key, mTmpRanking); + return mTmpRanking.getChannel(); + } + return null; + } + private void updateRankingAndSort(RankingMap ranking) { if (ranking != null) { mRankingMap = ranking; @@ -442,6 +452,7 @@ public class NotificationData { entry.notification.setOverrideGroupKey(overrideGroupKey); mGroupManager.onEntryUpdated(entry, oldSbn); } + entry.channel = getChannel(entry.key); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index c7adb60c25f3..83104e685e22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -72,11 +72,7 @@ public class NotificationGuts extends LinearLayout { private INotificationManager mINotificationManager; private int mStartingUserImportance; private StatusBarNotification mStatusBarNotification; - - private ImageView mAutoButton; - private TextView mImportanceSummary; - private TextView mImportanceTitle; - private boolean mAuto; + private NotificationChannel mNotificationChannel; private View mImportanceGroup; private View mChannelDisabled; @@ -170,11 +166,12 @@ public class NotificationGuts extends LinearLayout { } void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, - final StatusBarNotification sbn, OnSettingsClickListener onSettingsClick, + final StatusBarNotification sbn, final NotificationChannel channel, + OnSettingsClickListener onSettingsClick, OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) { mINotificationManager = iNotificationManager; + mNotificationChannel = channel; mStatusBarNotification = sbn; - final NotificationChannel channel = sbn.getNotificationChannel(); mStartingUserImportance = channel.getImportance(); final String pkg = sbn.getPackageName(); @@ -288,14 +285,13 @@ public class NotificationGuts extends LinearLayout { if (selectedImportance == mStartingUserImportance) { return; } - final NotificationChannel channel = mStatusBarNotification.getNotificationChannel(); MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, selectedImportance - mStartingUserImportance); - channel.setImportance(selectedImportance); + mNotificationChannel.setImportance(selectedImportance); try { mINotificationManager.updateNotificationChannelForPackage( mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(), - channel); + mNotificationChannel); } catch (RemoteException e) { // :( } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 93f72a8bdf78..f24e40b13578 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -72,6 +72,7 @@ public class CarStatusBar extends PhoneStatusBar implements SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener); registerPackageChangeReceivers(); + createBatteryController(); mCarBatteryController.startListening(); mConnectedDeviceSignalController.startListening(); } @@ -113,8 +114,7 @@ public class CarStatusBar extends PhoneStatusBar implements return statusBarView; } - @Override - protected BatteryController createBatteryController() { + private BatteryController createBatteryController() { mCarBatteryController = new CarBatteryController(mContext); mCarBatteryController.addBatteryViewHandler(this); return mCarBatteryController; 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 80ad9d2d780a..9612db038f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -989,10 +989,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - protected BatteryController createBatteryController() { - return new BatteryControllerImpl(mContext); - } - private void inflateShelf() { mNotificationShelf = (NotificationShelf) LayoutInflater.from(mContext).inflate( @@ -1392,7 +1388,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, newNotification.headsUpContentView = sbn.getNotification().headsUpContentView; StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(), - sbn.getOpPkg(), sbn.getNotificationChannel(), + sbn.getOpPkg(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java index c65f7150de0e..cac0806e0f15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java @@ -91,7 +91,6 @@ public class NotificationGutsTest { // mMockStatusBarNotification with a test channel. mNotificationChannel = new NotificationChannel( TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); - when(mMockStatusBarNotification.getNotificationChannel()).thenReturn(mNotificationChannel); when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME); } @@ -100,7 +99,7 @@ public class NotificationGutsTest { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); } @@ -109,7 +108,7 @@ public class NotificationGutsTest { @UiThreadTest public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -119,8 +118,8 @@ public class NotificationGutsTest { public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, (View v, int appUid) -> { latch.countDown(); }, - null, null); + mMockStatusBarNotification, mNotificationChannel, + (View v, int appUid) -> { latch.countDown(); }, null, null); final TextView settingsButton = (TextView) mNotificationGuts.findViewById(R.id.more_settings); @@ -134,7 +133,7 @@ public class NotificationGutsTest { public void testBindNotification_SetsOnClickListenerForDone() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, + mMockStatusBarNotification, mNotificationChannel, null, (View v) -> { latch.countDown(); }, null); @@ -148,7 +147,7 @@ public class NotificationGutsTest { @UiThreadTest public void testHasImportanceChanged_DefaultsToFalse() throws Exception { mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); assertFalse(mNotificationGuts.hasImportanceChanged()); } @@ -157,7 +156,7 @@ public class NotificationGutsTest { public void testHasImportanceChanged_ReturnsTrueAfterButtonChecked() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); // Find the high button and check it. RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); highButton.setChecked(true); @@ -169,7 +168,7 @@ public class NotificationGutsTest { public void testImportanceButtonCheckedBasedOnInitialImportance() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); assertTrue(highButton.isChecked()); @@ -179,7 +178,7 @@ public class NotificationGutsTest { @UiThreadTest public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); } @@ -189,7 +188,7 @@ public class NotificationGutsTest { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); highButton.setChecked(true); @@ -201,7 +200,7 @@ public class NotificationGutsTest { @UiThreadTest public void testCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); mNotificationGuts.closeControls(-1, -1, true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -213,7 +212,7 @@ public class NotificationGutsTest { public void testCloseControls_DoesNotUpdateNotificationChannelIfUnspecified() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); mNotificationGuts.closeControls(-1, -1, true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -225,7 +224,7 @@ public class NotificationGutsTest { public void testCloseControls_CallsUpdateNotificationChannelIfChanged() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); highButton.setChecked(true); @@ -240,7 +239,7 @@ public class NotificationGutsTest { public void testCloseControls_DoesNotUpdateNotificationChannelIfSaveFalse() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); highButton.setChecked(true); @@ -254,7 +253,7 @@ public class NotificationGutsTest { public void testEnabledSwitchOnByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); assertTrue(enabledSwitch.isChecked()); @@ -265,7 +264,7 @@ public class NotificationGutsTest { public void testEnabledSwitchVisibleByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); @@ -276,7 +275,8 @@ public class NotificationGutsTest { public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME)); + mMockStatusBarNotification, mNotificationChannel, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); @@ -287,7 +287,8 @@ public class NotificationGutsTest { public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME)); + mMockStatusBarNotification, mNotificationChannel, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); enabledSwitch.setChecked(false); @@ -301,7 +302,7 @@ public class NotificationGutsTest { public void testEnabledSwitchOverridesOtherButtons() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, - mMockStatusBarNotification, null, null, null); + mMockStatusBarNotification, mNotificationChannel, null, null, null); Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); RadioButton lowButton = (RadioButton) mNotificationGuts.findViewById(R.id.low_importance); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 88bc99fc14d7..96c468cd3249 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3311,6 +3311,16 @@ message MetricsEvent { // OS: 8.0 MANAGE_EXTERNAL_SOURCES = 808; + // ACTION: Logged when terms activity finishes. + // TIME: Indicates time taken by terms activity to finish in MS. + PROVISIONING_TERMS_ACTIVITY_TIME_MS = 809; + + // Indicates number of terms displayed on the terms screen. + PROVISIONING_TERMS_COUNT = 810; + + // Indicates number of terms read on the terms screen. + PROVISIONING_TERMS_READ = 811; + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index f718fa11598e..bee1f9729eea 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -243,7 +243,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private PendingIntent mImeSwitchPendingIntent; private boolean mShowOngoingImeSwitcherForPhones; private boolean mNotificationShown; - private final boolean mImeSelectedOnBoot; static class SessionState { final ClientState client; @@ -566,7 +565,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - class ImmsBroadcastReceiver extends android.content.BroadcastReceiver { + class ImmsBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); @@ -587,6 +586,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Intent.EXTRA_SETTING_NEW_VALUE); restoreEnabledInputMethods(mContext, prevValue, newValue); } + } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { + synchronized (mMethodMap) { + resetStateIfCurrentLocaleChangedLocked(); + } } else { Slog.w(TAG, "Unexpected intent " + intent); } @@ -845,9 +848,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } mSettings.switchCurrentUser(currentUserId, !mSystemReady); - // We need to rebuild IMEs. - buildInputMethodListLocked(false /* resetDefaultEnabledIme */); - updateInputMethodsFromSettingsLocked(true /* enabledChanged */); + if (mSystemReady) { + // We need to rebuild IMEs. + buildInputMethodListLocked(false /* resetDefaultEnabledIme */); + updateInputMethodsFromSettingsLocked(true /* enabledChanged */); + } } } @@ -897,13 +902,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mShowOngoingImeSwitcherForPhones = false; - final IntentFilter broadcastFilter = new IntentFilter(); - broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - broadcastFilter.addAction(Intent.ACTION_USER_ADDED); - broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); - broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED); - mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter); - mNotificationShown = false; int userId = 0; try { @@ -911,7 +909,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); } - mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true); // mSettings should be created before buildInputMethodListLocked mSettings = new InputMethodSettings( @@ -919,48 +916,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub updateCurrentProfileIds(); mFileManager = new InputMethodFileManager(mMethodMap, userId); - synchronized (mMethodMap) { - mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( - mSettings, context); - } - - // Just checking if defaultImiId is empty or not - final String defaultImiId = mSettings.getSelectedInputMethod(); - if (DEBUG) { - Slog.d(TAG, "Initial default ime = " + defaultImiId); - } - mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId); - - synchronized (mMethodMap) { - buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */); - } - mSettings.enableAllIMEsIfThereIsNoEnabledIME(); - - if (!mImeSelectedOnBoot) { - Slog.w(TAG, "No IME selected. Choose the most applicable IME."); - synchronized (mMethodMap) { - resetDefaultImeLocked(context); - } - } - - synchronized (mMethodMap) { - mSettingsObserver.registerContentObserverLocked(userId); - updateFromSettingsLocked(true); - } - - // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME - // according to the new system locale. - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_LOCALE_CHANGED); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - synchronized(mMethodMap) { - resetStateIfCurrentLocaleChangedLocked(); - } - } - }, filter); + mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( + mSettings, context); } private void resetDefaultImeLocked(Context context) { @@ -1089,6 +1046,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (!mSystemReady) { mSystemReady = true; + mLastSystemLocales = mRes.getConfiguration().getLocales(); final int currentUserId = mSettings.getCurrentUserId(); mSettings.switchCurrentUser(currentUserId, !mUserManager.isUserUnlockingOrUnlocked(currentUserId)); @@ -1105,14 +1063,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mWindowManagerInternal.setOnHardKeyboardStatusChangeListener( mHardKeyboardListener); } - if (!mImeSelectedOnBoot) { - Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here."); - resetStateIfCurrentLocaleChangedLocked(); - InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager, - mSettings.getEnabledInputMethodListLocked(), - mSettings.getCurrentUserId(), mContext.getBasePackageName()); - } - mLastSystemLocales = mRes.getConfiguration().getLocales(); + + mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true); + mSettingsObserver.registerContentObserverLocked(currentUserId); + + final IntentFilter broadcastFilter = new IntentFilter(); + broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + broadcastFilter.addAction(Intent.ACTION_USER_ADDED); + broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); + broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED); + broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED); + mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter); + + buildInputMethodListLocked(true /* resetDefaultEnabledIme */); + resetDefaultImeLocked(mContext); + updateFromSettingsLocked(true); + InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager, + mSettings.getEnabledInputMethodListLocked(), currentUserId, + mContext.getBasePackageName()); + try { startInputInnerLocked(); } catch (RuntimeException e) { @@ -2624,6 +2593,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // additional input method subtypes to the IME. if (TextUtils.isEmpty(imiId) || subtypes == null) return; synchronized (mMethodMap) { + if (!mSystemReady) { + return; + } final InputMethodInfo imi = mMethodMap.get(imiId); if (imi == null) return; final String[] packageInfos; @@ -3048,6 +3020,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme + " \n ------ caller=" + Debug.getCallers(10)); } + if (!mSystemReady) { + Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready"); + return; + } mMethodList.clear(); mMethodMap.clear(); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 94acd751c6c4..e11dd1aae400 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -33,6 +33,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IActivityManager; +import android.app.usage.StorageStatsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -3270,6 +3271,29 @@ class StorageManagerService extends IStorageManager.Stub } } + @Override + public long getCacheQuotaBytes(String volumeUuid, int uid) { + if (uid != Binder.getCallingUid()) { + mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG); + } + // TODO: wire up to cache quota once merged + return 64 * TrafficStats.MB_IN_BYTES; + } + + @Override + public long getCacheSizeBytes(String volumeUuid, int uid) { + if (uid != Binder.getCallingUid()) { + mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG); + } + final long token = Binder.clearCallingIdentity(); + try { + return mContext.getSystemService(StorageStatsManager.class) + .queryStatsForUid(volumeUuid, uid).getCacheBytes(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private void addObbStateLocked(ObbState obbState) throws RemoteException { final IBinder binder = obbState.getBinder(); List<ObbState> obbStates = mObbMounts.get(binder); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index df5f01d47cf7..49423b9e155e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6502,6 +6502,35 @@ public class AudioService extends IAudioService.Stub return mRecordMonitor.getActiveRecordingConfigurations(); } + public void disableRingtoneSync() { + final int callingUserId = UserHandle.getCallingUserId(); + final long token = Binder.clearCallingIdentity(); + try { + UserManager userManager = UserManager.get(mContext); + + // Disable the sync setting + Settings.Secure.putIntForUser(mContentResolver, + Settings.Secure.SYNC_PARENT_SOUNDS, 0 /* false */, callingUserId); + + UserInfo parentInfo = userManager.getProfileParent(callingUserId); + if (parentInfo != null && parentInfo.id != callingUserId) { + // This is a managed profile, so we clone the ringtones from the parent profile + cloneRingtoneSetting(callingUserId, parentInfo.id, Settings.System.RINGTONE); + cloneRingtoneSetting(callingUserId, parentInfo.id, + Settings.System.NOTIFICATION_SOUND); + cloneRingtoneSetting(callingUserId, parentInfo.id, Settings.System.ALARM_ALERT); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void cloneRingtoneSetting(int userId, int parentId, String ringtoneSetting) { + String parentSetting = Settings.System.getStringForUser(mContentResolver, ringtoneSetting, + parentId); + Settings.System.putStringForUser(mContentResolver, ringtoneSetting, parentSetting, userId); + } + //====================== // Audio playback notification //====================== diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java new file mode 100644 index 000000000000..4795fbf0ba3c --- /dev/null +++ b/services/core/java/com/android/server/notification/BadgeExtractor.java @@ -0,0 +1,59 @@ +/** +* Copyright (C) 2017 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; +import android.util.Slog; + +/** + * Determines whether a badge should be shown for this notification + */ +public class BadgeExtractor implements NotificationSignalExtractor { + private static final String TAG = "BadgeExtractor"; + private static final boolean DBG = false; + + private RankingConfig mConfig; + + public void initialize(Context ctx, NotificationUsageStats usageStats) { + if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + } + + public RankingReconsideration process(NotificationRecord record) { + if (record == null || record.getNotification() == null) { + if (DBG) Slog.d(TAG, "skipping empty notification"); + return null; + } + + if (mConfig == null) { + if (DBG) Slog.d(TAG, "missing config"); + return null; + } + boolean appCanShowBadge = + mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid()); + if (!appCanShowBadge) { + record.setShowBadge(false); + } else { + record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge); + } + + return null; + } + + @Override + public void setConfig(RankingConfig config) { + mConfig = config; + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 168884dbe189..96459be9234a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1243,6 +1243,35 @@ public class NotificationManagerService extends SystemService { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); } + private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel, + boolean fromAssistant) { + if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { + // cancel + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, + UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, + null); + } + if (fromAssistant) { + mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel); + } else { + mRankingHelper.updateNotificationChannel(pkg, uid, channel); + } + + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + for (int i = N - 1; i >= 0; --i) { + NotificationRecord r = mNotificationList.get(i); + if (channel.getId() != null && channel.getId().equals(r.getChannel().getId())) { + r.updateNotificationChannel(mRankingHelper.getNotificationChannel( + r.sbn.getPackageName(), r.getUser().getIdentifier(), + channel.getId(), false)); + } + } + } + mRankingHandler.requestSort(true); + savePolicyFile(); + } + private ArrayList<ComponentName> getSuppressors() { ArrayList<ComponentName> names = new ArrayList<ComponentName>(); for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) { @@ -1521,6 +1550,19 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean canShowBadge(String pkg, int uid) { + checkCallerIsSystem(); + return mRankingHelper.canShowBadge(pkg, uid); + } + + @Override + public void setShowBadge(String pkg, int uid, boolean showBadge) { + checkCallerIsSystem(); + mRankingHelper.setShowBadge(pkg, uid, showBadge); + savePolicyFile(); + } + + @Override public void createNotificationChannels(String pkg, ParceledListSlice channelsList) throws RemoteException { checkCallerIsSystemOrSameApp(pkg); @@ -1566,15 +1608,8 @@ public class NotificationManagerService extends SystemService { public void updateNotificationChannelForPackage(String pkg, int uid, NotificationChannel channel) { enforceSystemOrSystemUI("Caller not system or systemui"); - if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { - // cancel - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, - UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, - null); - } - mRankingHelper.updateNotificationChannel(pkg, uid, channel); - mRankingHandler.requestSort(true); - savePolicyFile(); + Preconditions.checkNotNull(channel); + updateNotificationChannelInt(pkg, uid, channel, false); } @Override @@ -1701,7 +1736,6 @@ public class NotificationManagerService extends SystemService { return new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), - sbn.getNotificationChannel(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), sbn.getNotification().clone(), sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); @@ -2495,15 +2529,9 @@ public class NotificationManagerService extends SystemService { public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg, NotificationChannel channel) throws RemoteException { ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token); - if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { - // cancel - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, - info.userid, REASON_CHANNEL_BANNED, null); - } + Preconditions.checkNotNull(channel); int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel); - mRankingHandler.requestSort(true); - savePolicyFile(); + updateNotificationChannelInt(pkg, uid, channel, true); } @Override @@ -2528,7 +2556,7 @@ public class NotificationManagerService extends SystemService { final ArrayList<SnoozeCriterion> snoozeCriterionList = adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA); if (!TextUtils.isEmpty(overrideChannelId)) { - n.setNotificationChannelOverride(mRankingHelper.getNotificationChannel( + n.updateNotificationChannel(mRankingHelper.getNotificationChannel( n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId, false /* includeDeleted */)); } @@ -2610,13 +2638,13 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification summarySbn = new StatusBarNotification(adjustedSbn.getPackageName(), adjustedSbn.getOpPkg(), - adjustedSbn.getNotificationChannel(), Integer.MAX_VALUE, GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(), adjustedSbn.getInitialPid(), summaryNotification, adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY, System.currentTimeMillis()); - summaryRecord = new NotificationRecord(getContext(), summarySbn); + summaryRecord = new NotificationRecord(getContext(), summarySbn, + notificationRecord.getChannel()); summaries.put(pkg, summarySbn.getKey()); } } @@ -2882,7 +2910,7 @@ public class NotificationManagerService extends SystemService { final NotificationChannel channel = mRankingHelper.getNotificationChannelWithFallback(pkg, callingUid, notification.getChannel(), false /* includeDeleted */); final StatusBarNotification n = new StatusBarNotification( - pkg, opPkg, channel, id, tag, callingUid, callingPid, notification, + pkg, opPkg, id, tag, callingUid, callingPid, notification, user, null, System.currentTimeMillis()); // Limit the number of notifications that any given package except the android @@ -2946,7 +2974,7 @@ public class NotificationManagerService extends SystemService { Notification.PRIORITY_MAX); // setup local book-keeping - final NotificationRecord r = new NotificationRecord(getContext(), n); + final NotificationRecord r = new NotificationRecord(getContext(), n, channel); synchronized (mNotificationLock) { mEnqueuedNotifications.add(r); } @@ -3490,14 +3518,18 @@ public class NotificationManagerService extends SystemService { boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj; synchronized (mNotificationLock) { final int N = mNotificationList.size(); + // Any field that can change via one of the extractors or by the assistant + // needs to be added here. ArrayList<String> orderBefore = new ArrayList<String>(N); ArrayList<String> groupOverrideBefore = new ArrayList<>(N); int[] visibilities = new int[N]; + boolean[] showBadges = new boolean[N]; for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); orderBefore.add(r.getKey()); groupOverrideBefore.add(r.sbn.getGroupKey()); visibilities[i] = r.getPackageVisibilityOverride(); + showBadges[i] = r.canShowBadge(); mRankingHelper.extractSignals(r); } mRankingHelper.sort(mNotificationList); @@ -3506,7 +3538,8 @@ public class NotificationManagerService extends SystemService { if (forceUpdate || !orderBefore.get(i).equals(r.getKey()) || visibilities[i] != r.getPackageVisibilityOverride() - || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) { + || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey()) + || showBadges[i] != r.canShowBadge()) { scheduleSendRankingUpdate(); return; } @@ -4246,9 +4279,10 @@ public class NotificationManagerService extends SystemService { Bundle visibilityOverrides = new Bundle(); Bundle suppressedVisualEffects = new Bundle(); Bundle explanation = new Bundle(); - Bundle overrideChannels = new Bundle(); + Bundle channels = new Bundle(); Bundle overridePeople = new Bundle(); Bundle snoozeCriteria = new Bundle(); + Bundle showBadge = new Bundle(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -4270,9 +4304,10 @@ public class NotificationManagerService extends SystemService { visibilityOverrides.putInt(key, record.getPackageVisibilityOverride()); } overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey()); - overrideChannels.putParcelable(key, record.getChannel()); + channels.putParcelable(key, record.getChannel()); overridePeople.putStringArrayList(key, record.getPeopleOverride()); snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria()); + showBadge.putBoolean(key, record.canShowBadge()); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); @@ -4283,7 +4318,7 @@ public class NotificationManagerService extends SystemService { } return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, - overrideChannels, overridePeople, snoozeCriteria); + channels, overridePeople, snoozeCriteria, showBadge); } private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index e8c3d97747d7..2a5a25f8e866 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -25,7 +25,6 @@ import android.app.Notification; import android.app.NotificationChannel; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; @@ -114,12 +113,14 @@ public final class NotificationRecord { private Uri mSound; private long[] mVibration; private AudioAttributes mAttributes; - private NotificationChannel mOverrideChannel; + private NotificationChannel mChannel; private ArrayList<String> mPeopleOverride; private ArrayList<SnoozeCriterion> mSnoozeCriteria; + private boolean mShowBadge; @VisibleForTesting - public NotificationRecord(Context context, StatusBarNotification sbn) + public NotificationRecord(Context context, StatusBarNotification sbn, + NotificationChannel channel) { this.sbn = sbn; mOriginalFlags = sbn.getNotification().flags; @@ -128,6 +129,7 @@ public final class NotificationRecord { mUpdateTimeMs = mCreationTimeMs; mContext = context; stats = new NotificationUsageStats.SingleNotificationStats(); + mChannel = channel; mPreChannelsNotification = isPreChannelsNotification(); mSound = calculateSound(); mVibration = calculateVibration(); @@ -154,7 +156,7 @@ public final class NotificationRecord { private Uri calculateSound() { final Notification n = sbn.getNotification(); - Uri sound = sbn.getNotificationChannel().getSound(); + Uri sound = mChannel.getSound(); if (mPreChannelsNotification && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) { @@ -386,7 +388,8 @@ public final class NotificationRecord { pw.println(prefix + " mSound= " + mSound); pw.println(prefix + " mVibration= " + mVibration); pw.println(prefix + " mAttributes= " + mAttributes); - pw.println(prefix + " overrideChannel=" + getChannel()); + pw.println(prefix + " mShowBadge=" + mShowBadge); + pw.println(prefix + " channel=" + getChannel()); if (getPeopleOverride() != null) { pw.println(prefix + " overridePeople= " + TextUtils.join(",", getPeopleOverride())); } @@ -640,16 +643,24 @@ public final class NotificationRecord { } public NotificationChannel getChannel() { - return mOverrideChannel == null ? sbn.getNotificationChannel() : mOverrideChannel; + return mChannel; } - protected void setNotificationChannelOverride(NotificationChannel channel) { - mOverrideChannel = channel; - if (mOverrideChannel != null) { + protected void updateNotificationChannel(NotificationChannel channel) { + if (channel != null) { + mChannel = channel; calculateImportance(); } } + public void setShowBadge(boolean showBadge) { + mShowBadge = showBadge; + } + + public boolean canShowBadge() { + return mShowBadge; + } + public Uri getSound() { return mSound; } diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index c2cef09e0599..492d5c639a7d 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -22,6 +22,8 @@ public interface RankingConfig { void setImportance(String packageName, int uid, int importance); int getImportance(String packageName, int uid); + void setShowBadge(String packageName, int uid, boolean showBadge); + boolean canShowBadge(String packageName, int uid); void createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp); diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index e44fb7f6b31c..1861bcb7df85 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -67,10 +67,12 @@ public class RankingHelper implements RankingConfig { private static final String ATT_PRIORITY = "priority"; private static final String ATT_VISIBILITY = "visibility"; private static final String ATT_IMPORTANCE = "importance"; + private static final String ATT_SHOW_BADGE = "show_badge"; private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT; private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; + private static final boolean DEFAULT_SHOW_BADGE = true; private final NotificationSignalExtractor[] mSignalExtractors; private final NotificationComparator mPreliminaryComparator; @@ -169,7 +171,8 @@ public class RankingHelper implements RankingConfig { Record r = getOrCreateRecord(name, uid, safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY), - safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); + safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), + safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE)); // Channels final int innerDepth = parser.getDepth(); @@ -215,11 +218,11 @@ public class RankingHelper implements RankingConfig { private Record getOrCreateRecord(String pkg, int uid) { return getOrCreateRecord(pkg, uid, - DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY); + DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE); } private Record getOrCreateRecord(String pkg, int uid, int importance, int priority, - int visibility) { + int visibility, boolean showBadge) { final String key = recordKey(pkg, uid); Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(key); if (r == null) { @@ -229,6 +232,7 @@ public class RankingHelper implements RankingConfig { r.importance = importance; r.priority = priority; r.visibility = visibility; + r.showBadge = showBadge; createDefaultChannelIfMissing(r); if (r.uid == Record.UNKNOWN_UID) { mRestoredWithoutUids.put(pkg, r); @@ -298,7 +302,7 @@ public class RankingHelper implements RankingConfig { } final boolean hasNonDefaultSettings = r.importance != DEFAULT_IMPORTANCE || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY - || r.channels.size() > 0; + || r.showBadge != DEFAULT_SHOW_BADGE || r.channels.size() > 0; if (hasNonDefaultSettings) { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); @@ -311,6 +315,7 @@ public class RankingHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } + out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); if (!forBackup) { out.attribute(null, ATT_UID, Integer.toString(r.uid)); @@ -396,6 +401,12 @@ public class RankingHelper implements RankingConfig { return Collections.binarySearch(notificationList, target, mFinalComparator); } + private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { + final String value = parser.getAttributeValue(null, att); + if (TextUtils.isEmpty(value)) return defValue; + return Boolean.parseBoolean(value); + } + private static int safeInt(XmlPullParser parser, String att, int defValue) { final String val = parser.getAttributeValue(null, att); return tryParseInt(val, defValue); @@ -419,6 +430,17 @@ public class RankingHelper implements RankingConfig { } @Override + public boolean canShowBadge(String packageName, int uid) { + return getOrCreateRecord(packageName, uid).showBadge; + } + + @Override + public void setShowBadge(String packageName, int uid, boolean showBadge) { + getOrCreateRecord(packageName, uid).showBadge = showBadge; + updateConfig(); + } + + @Override public void createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp) { Preconditions.checkNotNull(pkg); @@ -454,6 +476,9 @@ public class RankingHelper implements RankingConfig { if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE); } + if (!r.showBadge) { + channel.setShowBadge(false); + } r.channels.put(channel.getId(), channel); updateConfig(); } @@ -672,7 +697,7 @@ public class RankingHelper implements RankingConfig { final Record r = records.valueAt(i); if (filter == null || filter.matches(r.pkg)) { pw.print(prefix); - pw.print(" "); + pw.print(" AppSettings: "); pw.print(r.pkg); pw.print(" ("); pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid)); @@ -689,6 +714,8 @@ public class RankingHelper implements RankingConfig { pw.print(" visibility="); pw.print(Notification.visibilityToString(r.visibility)); } + pw.print(" showBadge="); + pw.print(Boolean.toString(r.showBadge)); pw.println(); for (NotificationChannel channel : r.channels.values()) { pw.print(prefix); @@ -725,6 +752,9 @@ public class RankingHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { record.put("visibility", Notification.visibilityToString(r.visibility)); } + if (r.showBadge != DEFAULT_SHOW_BADGE) { + record.put("showBadge", Boolean.valueOf(r.showBadge)); + } for (NotificationChannel channel : r.channels.values()) { record.put("channel", channel.toJson()); } @@ -838,6 +868,7 @@ public class RankingHelper implements RankingConfig { int importance = DEFAULT_IMPORTANCE; int priority = DEFAULT_PRIORITY; int visibility = DEFAULT_VISIBILITY; + boolean showBadge = DEFAULT_SHOW_BADGE; ArrayMap<String, NotificationChannel> channels = new ArrayMap<>(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7b32d20d76dc..63a5d145fe62 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17703,6 +17703,13 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } + try { + // update shared libraries for the newly re-installed system package + updateSharedLibrariesLPr(newPkg, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); + } + prepareAppDataAfterInstallLIF(newPkg); // writer diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 623a0a59fd1a..f3b013122890 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5574,7 +5574,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Set whether auto time is required by the specified admin (must be device owner). + * Set whether auto time is required by the specified admin (must be device or profile owner). */ @Override public void setAutoTimeRequired(ComponentName who, boolean required) { @@ -5585,7 +5585,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = UserHandle.getCallingUserId(); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.requireAutoTime != required) { admin.requireAutoTime = required; saveSettingsLocked(userHandle); @@ -5604,7 +5604,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Returns whether or not auto time is required by the device owner. + * Returns whether or not auto time is required by the device owner or any profile owner. */ @Override public boolean getAutoTimeRequired() { @@ -5613,7 +5613,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } synchronized (this) { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - return (deviceOwner != null) ? deviceOwner.requireAutoTime : false; + if (deviceOwner != null && deviceOwner.requireAutoTime) { + // If the device owner enforces auto time, we don't need to check the PO's + return true; + } + + // Now check to see if any profile owner on any user enforces auto time + for (Integer userId : mOwners.getProfileOwnerKeys()) { + ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); + if (profileOwner != null && profileOwner.requireAutoTime) { + return true; + } + } + + return false; } } diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java new file mode 100644 index 000000000000..b26bac3188eb --- /dev/null +++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2017 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 static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.Notification.Builder; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BadgeExtractorTest { + + @Mock RankingConfig mConfig; + + private String mPkg = "com.android.server.notification"; + private int mId = 1001; + private String mTag = null; + private int mUid = 1000; + private int mPid = 2000; + private UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + private NotificationRecord getNotificationRecord(NotificationChannel channel) { + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setPriority(Notification.PRIORITY_HIGH) + .setDefaults(Notification.DEFAULT_SOUND); + + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, + mPid, n, mUser, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + return r; + } + + private Context getContext() { + return InstrumentationRegistry.getTargetContext(); + } + + // + // Tests + // + + @Test + public void testAppYesChannelNo() throws Exception { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true); + NotificationChannel channel = + new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED); + channel.setShowBadge(false); + + NotificationRecord r = getNotificationRecord(channel); + + extractor.process(r); + + assertFalse(r.canShowBadge()); + } + + @Test + public void testAppNoChannelYes() throws Exception { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false); + NotificationChannel channel = + new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH); + channel.setShowBadge(true); + + NotificationRecord r = getNotificationRecord(channel); + + extractor.process(r); + + assertFalse(r.canShowBadge()); + } + + @Test + public void testAppYesChannelYes() throws Exception { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true); + NotificationChannel channel = + new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED); + channel.setShowBadge(true); + + NotificationRecord r = getNotificationRecord(channel); + + extractor.process(r); + + assertTrue(r.canShowBadge()); + } + + @Test + public void testAppNoChannelNo() throws Exception { + BadgeExtractor extractor = new BadgeExtractor(); + extractor.setConfig(mConfig); + + when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false); + NotificationChannel channel = + new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED); + channel.setShowBadge(false); + + NotificationRecord r = getNotificationRecord(channel); + + extractor.process(r); + + assertFalse(r.canShowBadge()); + } +} diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index ad436724a37e..468a26b57d7e 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -230,9 +230,9 @@ public class BuzzBeepBlinkTest { n.flags |= Notification.FLAG_INSISTENT; } - StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, id, mTag, mUid, + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); mService.addNotification(r); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java index f48d785b660d..936531b07059 100644 --- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java @@ -70,9 +70,7 @@ public class GroupHelperTest { if (groupKey != null) { nb.setGroup(groupKey); } - NotificationChannel channel = - new NotificationChannel("test", "test", NotificationManager.IMPORTANCE_LOW); - return new StatusBarNotification(pkg, pkg, channel, id, tag, 0, 0, nb.build(), user, null, + return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, nb.build(), user, null, System.currentTimeMillis()); } diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java index eee9cf19bb7f..f8a32bbeaf79 100644 --- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java +++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java @@ -69,9 +69,9 @@ public class ImportanceExtractorTest { .setDefaults(Notification.DEFAULT_SOUND); Notification n = builder.build(); - StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, mId, mTag, mUid, + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java index 403b65c44a2f..aa08b41d69c0 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java @@ -105,8 +105,8 @@ public class NotificationComparatorTest { .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, - callPkg, getDefaultChannel(), 1, "minCall", callUid, callUid, n1, - new UserHandle(userId), "", 2000)); + callPkg, 1, "minCall", callUid, callUid, n1, + new UserHandle(userId), "", 2000), getDefaultChannel()); mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN); Notification n2 = new Notification.Builder(mContext) @@ -114,8 +114,8 @@ public class NotificationComparatorTest { .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, - callPkg, getDefaultChannel(), 1, "highcall", callUid, callUid, n2, - new UserHandle(userId), "", 1999)); + callPkg, 1, "highcall", callUid, callUid, n2, + new UserHandle(userId), "", 1999), getDefaultChannel()); mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH); Notification n3 = new Notification.Builder(mContext) @@ -124,43 +124,43 @@ public class NotificationComparatorTest { .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "media", uid2, uid2, n3, new UserHandle(userId), - "", 1499)); + pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId), + "", 1499), getDefaultChannel()); mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n4 = new Notification.Builder(mContext) .setStyle(new Notification.MessagingStyle("sender!")).build(); mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId), - "", 1599)); + pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId), + "", 1599), getDefaultChannel()); mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH); mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX); Notification n5 = new Notification.Builder(mContext) .setCategory(Notification.CATEGORY_MESSAGE).build(); mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg, - smsPkg, getDefaultChannel(), 1, "sms", smsUid, smsUid, n5, new UserHandle(userId), - "", 1299)); + smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId), + "", 1299), getDefaultChannel()); mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n6 = new Notification.Builder(mContext).build(); mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "starred", uid2, uid2, n6, new UserHandle(userId), - "", 1259)); + pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId), + "", 1259), getDefaultChannel()); mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT); mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n7 = new Notification.Builder(mContext).build(); mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "contact", uid2, uid2, n7, new UserHandle(userId), - "", 1259)); + pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId), + "", 1259), getDefaultChannel()); mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT); mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); Notification n8 = new Notification.Builder(mContext).build(); mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "urgent", uid2, uid2, n8, new UserHandle(userId), - "", 1258)); + pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId), + "", 1258), getDefaultChannel()); mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH); Notification n9 = new Notification.Builder(mContext) @@ -169,15 +169,15 @@ public class NotificationComparatorTest { |Notification.FLAG_FOREGROUND_SERVICE, true) .build(); mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "cheater", uid2, uid2, n9, new UserHandle(userId), - "", 9258)); + pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId), + "", 9258), getDefaultChannel()); mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW); Notification n10 = new Notification.Builder(mContext) .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build(); mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2, - pkg2, getDefaultChannel(), 1, "email", uid2, uid2, n10, new UserHandle(userId), - "", 1599)); + pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId), + "", 1599), getDefaultChannel()); mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH); } diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java index b6166f6eb8b5..f0f4c4d66936 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -59,6 +59,7 @@ public class NotificationListenerServiceTest { assertEquals(getChannel(key, i), ranking.getChannel()); assertEquals(getPeople(key, i), ranking.getAdditionalPeople()); assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria()); + assertEquals(getShowBadge(i), ranking.canShowBadge()); } } @@ -68,9 +69,10 @@ public class NotificationListenerServiceTest { Bundle overrideGroupKeys = new Bundle(); Bundle suppressedVisualEffects = new Bundle(); Bundle explanation = new Bundle(); - Bundle overrideChannels = new Bundle(); + Bundle channels = new Bundle(); Bundle overridePeople = new Bundle(); Bundle snoozeCriteria = new Bundle(); + Bundle showBadge = new Bundle(); int[] importance = new int[mKeys.length]; for (int i = 0; i < mKeys.length; i++) { @@ -83,14 +85,15 @@ public class NotificationListenerServiceTest { suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i)); importance[i] = getImportance(i); explanation.putString(key, getExplanation(key)); - overrideChannels.putParcelable(key, getChannel(key, i)); + channels.putParcelable(key, getChannel(key, i)); overridePeople.putStringArrayList(key, getPeople(key, i)); snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i)); + showBadge.putBoolean(key, getShowBadge(i)); } NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, interceptedKeys.toArray(new String[0]), visibilityOverrides, suppressedVisualEffects, importance, explanation, overrideGroupKeys, - overrideChannels, overridePeople, snoozeCriteria); + channels, overridePeople, snoozeCriteria, showBadge); return update; } @@ -122,6 +125,10 @@ public class NotificationListenerServiceTest { return new NotificationChannel(key, key, getImportance(index)); } + private boolean getShowBadge(int index) { + return index % 3 == 0; + } + private ArrayList<String> getPeople(String key, int index) { ArrayList<String> people = new ArrayList<>(); for (int i = 0; i < index; i++) { diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 9b74fcc864e9..92d9810f7c3f 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -132,9 +132,9 @@ public class NotificationManagerServiceTest { .setPriority(Notification.PRIORITY_HIGH) .build(); StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(), - mContext.getPackageName(), channel, 1, "tag", uid, 0, + mContext.getPackageName(), 1, "tag", uid, 0, n, new UserHandle(uid), null, 0); - return new NotificationRecord(mContext, sbn); + return new NotificationRecord(mContext, sbn, channel); } @Test diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java index fc94271f8f86..15dcc266bbb7 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java @@ -136,10 +136,10 @@ public class NotificationRecordTest { Notification n = builder.build(); if (preO) { - return new StatusBarNotification(pkg, pkg, defaultChannel, id1, tag1, uid, uid, n, + return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); } else { - return new StatusBarNotification(pkg2, pkg2, channel, id2, tag2, uid2, uid2, n, + return new StatusBarNotification(pkg2, pkg2, id2, tag2, uid2, uid2, n, mUser, null, uid2); } } @@ -155,7 +155,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound()); } @@ -166,7 +166,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(CUSTOM_SOUND, record.getSound()); } @@ -178,7 +178,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(CUSTOM_SOUND, record.getSound()); } @@ -189,7 +189,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); assertEquals(CUSTOM_SOUND, record.getSound()); } @@ -200,7 +200,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertNotNull(record.getVibration()); } @@ -211,7 +211,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(CUSTOM_VIBRATION, record.getVibration()); } @@ -223,7 +223,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration())); } @@ -234,7 +234,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */, false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration()); } @@ -245,7 +245,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes()); } @@ -256,7 +256,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes()); } @@ -264,7 +264,7 @@ public class NotificationRecordTest { public void testImportance_preUpgrade() throws Exception { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance()); } @@ -275,7 +275,7 @@ public class NotificationRecordTest { StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance()); } @@ -283,7 +283,7 @@ public class NotificationRecordTest { public void testImportance_upgrade() throws Exception { StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */); - NotificationRecord record = new NotificationRecord(mMockContext, sbn); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance()); } } diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 59ac427f4251..0320d8acad74 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -105,8 +105,8 @@ public class RankingHelperTest { .setWhen(1205) .build(); mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortA, user, - null, System.currentTimeMillis())); + "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user, + null, System.currentTimeMillis()), getDefaultChannel()); mNotiGroupGSortB = new Notification.Builder(getContext()) .setContentTitle("B") @@ -115,24 +115,24 @@ public class RankingHelperTest { .setWhen(1200) .build(); mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortB, user, - null, System.currentTimeMillis())); + "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user, + null, System.currentTimeMillis()), getDefaultChannel()); mNotiNoGroup = new Notification.Builder(getContext()) .setContentTitle("C") .setWhen(1201) .build(); mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup, user, - null, System.currentTimeMillis())); + "package", "package", 1, null, 0, 0, mNotiNoGroup, user, + null, System.currentTimeMillis()), getDefaultChannel()); mNotiNoGroup2 = new Notification.Builder(getContext()) .setContentTitle("D") .setWhen(1202) .build(); mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup2, user, - null, System.currentTimeMillis())); + "package", "package", 1, null, 0, 0, mNotiNoGroup2, user, + null, System.currentTimeMillis()), getDefaultChannel()); mNotiNoGroupSortA = new Notification.Builder(getContext()) .setContentTitle("E") @@ -140,8 +140,8 @@ public class RankingHelperTest { .setSortKey("A") .build(); mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroupSortA, user, - null, System.currentTimeMillis())); + "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user, + null, System.currentTimeMillis()), getDefaultChannel()); final ApplicationInfo legacy = new ApplicationInfo(); legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; @@ -254,8 +254,12 @@ public class RankingHelperTest { mHelper.createNotificationChannel(pkg, uid, channel1, true); mHelper.createNotificationChannel(pkg, uid, channel2, false); + mHelper.setShowBadge(pkg, uid, true); + mHelper.setShowBadge(pkg2, uid2, false); + ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); + mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{pkg}, new int[]{uid}); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), @@ -263,6 +267,8 @@ public class RankingHelperTest { parser.nextTag(); mHelper.readXml(parser, false); + assertFalse(mHelper.canShowBadge(pkg2, uid2)); + assertTrue(mHelper.canShowBadge(pkg, uid)); assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false)); compareChannels(channel2, mHelper.getNotificationChannel(pkg, uid, channel2.getId(), false)); @@ -758,4 +764,11 @@ public class RankingHelperTest { mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid}); assertEquals(2, mHelper.getNotificationChannels(pkg, uid, false).getList().size()); } + + @Test + public void testRecordDefaults() throws Exception { + assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid)); + assertEquals(true, mHelper.canShowBadge(pkg, uid)); + assertEquals(1, mHelper.getNotificationChannels(pkg, uid, false).getList().size()); + } } diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java index 460fcdfd960d..b7931d4f6a01 100644 --- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java @@ -199,8 +199,8 @@ public class SnoozeHelperTest { .setWhen(1205) .build(); return new NotificationRecord(getContext(), new StatusBarNotification( - pkg, pkg, getDefaultChannel(), id, tag, 0, 0, n, user, null, - System.currentTimeMillis())); + pkg, pkg, id, tag, 0, 0, n, user, null, + System.currentTimeMillis()), getDefaultChannel()); } private NotificationChannel getDefaultChannel() { diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 68765b643c66..68269751efc1 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -37,6 +37,7 @@ import android.os.storage.VolumeInfo; import android.util.Slog; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; @@ -46,8 +47,6 @@ public class StorageStatsService extends IStorageStatsManager.Stub { private static final String PROP_VERIFY_STORAGE = "fw.verify_storage"; - // TODO: pivot all methods to manual mode when quota isn't supported - public static class Lifecycle extends SystemService { private StorageStatsService mService; @@ -71,11 +70,11 @@ public class StorageStatsService extends IStorageStatsManager.Stub { private final Installer mInstaller; public StorageStatsService(Context context) { - mContext = context; - mAppOps = context.getSystemService(AppOpsManager.class); - mUser = context.getSystemService(UserManager.class); - mPackage = context.getSystemService(PackageManager.class); - mStorage = context.getSystemService(StorageManager.class); + mContext = Preconditions.checkNotNull(context); + mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class)); + mUser = Preconditions.checkNotNull(context.getSystemService(UserManager.class)); + mPackage = Preconditions.checkNotNull(context.getPackageManager()); + mStorage = Preconditions.checkNotNull(context.getSystemService(StorageManager.class)); mInstaller = new Installer(context); mInstaller.onStart(); @@ -107,7 +106,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { case AppOpsManager.MODE_ALLOWED: return; case AppOpsManager.MODE_DEFAULT: - mContext.enforceCallingPermission( + mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, TAG); return; default: |