Merge "Create Public API which exposes if the device is in a state where simultaneous voice and data are possible."
diff --git a/api/current.txt b/api/current.txt
index 9cd3ee3..243c78f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -203,6 +203,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 ebe0660..063c3c3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -312,6 +312,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 df82a8a..4fcaf69 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -203,6 +203,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 556d7ad..a9d1cf6 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -78,8 +78,6 @@
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 @@
private VoiceInteractor mVoiceInteractor;
- private TextAssistant mTextAssistant;
-
private CharSequence mTitle;
private int mTitleColor = 0;
@@ -1398,24 +1394,6 @@
}
/**
- * 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 f909af0..d674bfe 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -47,6 +47,8 @@
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 6793c90..c0bf0c4 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -24,25 +24,25 @@
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.Looper;
-import android.os.PowerManager;
-import android.os.RemoteException;
import android.os.IBinder;
-import android.os.IUserManager;
+import android.os.Looper;
+import android.os.RemoteException;
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 @@
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 @@
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 601dfce..82917d2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1051,7 +1051,7 @@
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 @@
*/
@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 @@
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 @@
* @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 @@
* @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 56ef791..be5f80a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -122,6 +122,7 @@
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 @@
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 @@
/**
* 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 0000000..1f015a6
--- /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 a37f22b..5d8909c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -119,7 +119,6 @@
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.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 @@
}});
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 0da89eb..aa56be6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3735,13 +3735,13 @@
}
/**
- * 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 f41d7f2..38e6fbe 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -34,9 +34,7 @@
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.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 @@
/**
* 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 8cc9a3a..c550094 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4842,6 +4842,10 @@
* 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 5cd15dd..b091d7e 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -72,6 +72,11 @@
*/
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 d8d7abe..ca3011e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3899,6 +3899,9 @@
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 @@
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 @@
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 b03c907..35a266b 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -291,4 +291,6 @@
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 c6ff476..626d6f4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -24,27 +24,32 @@
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.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 @@
}
}
+ /**
+ * 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 694837e..d930689 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1166,11 +1166,12 @@
// 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 @@
}
/**
- * 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 @@
* 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 @@
}
/**
- * 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 @@
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 @@
mImportance = importance;
mImportanceExplanation = explanation;
mOverrideGroupKey = overrideGroupKey;
- mOverrideChannel = overrideChannel;
+ mChannel = channel;
mOverridePeople = overridePeople;
mSnoozeCriteria = snoozeCriteria;
+ mShowBadge = showBadge;
}
/**
@@ -1343,9 +1353,10 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
}
}
+ // 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 a2cdeff..326b212 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -31,14 +31,16 @@
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 @@
mImportance = importance;
mImportanceExplanation = explanation;
mOverrideGroupKeys = overrideGroupKeys;
- mOverrideChannels = overrideChannels;
+ mChannels = channels;
mOverridePeople = overridePeople;
mSnoozeCriteria = snoozeCriteria;
+ mShowBadge = showBadge;
}
public NotificationRankingUpdate(Parcel in) {
@@ -60,9 +63,10 @@
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 @@
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 @@
return mOverrideGroupKeys;
}
- public Bundle getOverrideChannels() {
- return mOverrideChannels;
+ public Bundle getChannels() {
+ return mChannels;
}
public Bundle getOverridePeople() {
@@ -134,4 +139,8 @@
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 6276af3..85baf4e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -43,21 +43,18 @@
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 @@
this.postTime = postTime;
this.key = key();
this.groupKey = groupKey();
- this.channel = null;
}
public StatusBarNotification(Parcel in) {
@@ -112,7 +108,6 @@
}
this.key = key();
this.groupKey = groupKey();
- this.channel = NotificationChannel.CREATOR.createFromParcel(in);
}
private String key() {
@@ -182,7 +177,6 @@
} else {
out.writeInt(0);
}
- this.channel.writeToParcel(out, flags);
}
public int describeContents() {
@@ -209,14 +203,14 @@
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 @@
}
/**
- * 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 b044981..0000000
--- 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 bb226da..0000000
--- 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 d4548f0..0000000
--- 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 eb834f1..0000000
--- 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 9400458..0000000
--- 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 b37ea8e..105cc47 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -292,7 +292,7 @@
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 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 @@
/**
* Returns the display's HDR capabilities.
+ *
+ * @see #isHdr()
*/
public HdrCapabilities getHdrCapabilities() {
synchronized (this) {
@@ -754,6 +769,35 @@
}
/**
+ * 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 a07a7ef..41a13cf 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 @@
}
/**
- * 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 @@
}
}
- 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 @@
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);
+ // Return the next non-default cluster if we can find it.
+ return clusters.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);
- }
+ // 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);
+ // Return the previous non-default cluster if we can find it.
+ return clusters.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);
- }
+ // 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 26e311c..13555f4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1252,14 +1252,6 @@
@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 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 @@
* 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 @@
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 @@
*/
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 @@
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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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 d252d75..480741e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -915,13 +915,10 @@
*/
@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 @@
@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 @@
}
@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 @@
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 @@
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 c9277ca..79b05cd 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import android.os.Bundle;
-import android.view.View.KeyboardNavigationGroupType;
import android.view.accessibility.AccessibilityEvent;
/**
@@ -148,20 +147,17 @@
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 c0f2c37..3cbe82e 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.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 @@
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 @@
}
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 @@
// 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 @@
* {@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 0000000..7aab71f
--- /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 0000000..3acbdc0
--- /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 0000000..b5ab4bf
--- /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 0000000..6af0efb
--- /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 0000000..b84e2ae
--- /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 0000000..d94d163
--- /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 0000000..3172c13
--- /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 2fc8ec9..f7f9a81 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -60,8 +60,6 @@
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.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 @@
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 @@
private boolean mPreserveSelection;
private boolean mRestartActionModeOnNextRefresh;
+ private TextClassificationResult mTextClassificationResult;
+
boolean mIsBeingLongClicked;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
@@ -1889,7 +1891,7 @@
mInsertionPointCursorController.invalidateHandle();
}
if (mTextActionMode != null) {
- mTextActionMode.invalidate();
+ invalidateActionMode(getTextClassifierInfo(false));
}
}
@@ -1982,12 +1984,12 @@
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 @@
*
* @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 @@
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 @@
}
if (mTextActionMode != null) {
// Text action mode is already started
- mTextActionMode.invalidate();
+ invalidateActionMode(getTextClassifierInfo(false));
return false;
}
@@ -3744,8 +3781,7 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
}
}
- 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 @@
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 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 @@
}
positionAtCursorOffset(offset, false);
if (mTextActionMode != null) {
- mTextActionMode.invalidate();
+ invalidateActionMode(getTextClassifierInfo(false));
}
}
@@ -4817,7 +4848,7 @@
}
updateDrawable();
if (mTextActionMode != null) {
- mTextActionMode.invalidate();
+ invalidateActionMode(getTextClassifierInfo(false));
}
}
@@ -5465,7 +5496,8 @@
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 b1fc67e..2f303cd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -77,8 +77,6 @@
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.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 @@
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 @@
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 @@
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 @@
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;
+ @NonNull
+ public TextClassifier getTextClassifier() {
+ if (mTextClassifier == null) {
+ TextClassificationManager tcm =
+ mContext.getSystemService(TextClassificationManager.class);
+ if (tcm != null) {
+ mTextClassifier = tcm.getDefaultTextClassifier();
+ } else {
+ mTextClassifier = TextClassifier.NO_OP;
+ }
}
- 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);
- }
- 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 8d11783..a94b161 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -95,39 +95,32 @@
}
}
+ 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;
+ int result = compareNullableCharSequences(mImeName, other.mImeName);
+ if (result != 0) {
+ return result;
}
- if (TextUtils.isEmpty(other.mImeName)) {
- return -1;
+ result = compareNullableCharSequences(mSubtypeName, other.mSubtypeName);
+ if (result != 0) {
+ return result;
}
- if (!TextUtils.equals(mImeName, other.mImeName)) {
- return mImeName.toString().compareTo(other.mImeName.toString());
+ result = (mIsSystemLocale ? -1 : 0) - (other.mIsSystemLocale ? -1 : 0);
+ if (result != 0) {
+ return result;
}
- 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;
- }
- if (TextUtils.isEmpty(mSubtypeName)) {
- return 1;
- }
- if (TextUtils.isEmpty(other.mSubtypeName)) {
- return -1;
- }
- 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 716997f..c08cd72 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -1080,22 +1080,6 @@
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 7eda3da..2d78979 100644
--- a/core/java/com/android/internal/logging/LogBuilder.java
+++ b/core/java/com/android/internal/logging/LogBuilder.java
@@ -23,6 +23,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
/**
* Helper class to assemble more complex logs.
*
@@ -31,6 +32,13 @@
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 @@
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 @@
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 16c2719..b90336c 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -94,9 +94,6 @@
}
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 87c2b25..df7a5f5 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 5235116..dfa672d 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 7de48d3..c36279c 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 f351b70..613616f 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 060c59e..30da26b 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 34c34d7..dc75417 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.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 @@
}
}
+ 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 @@
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 a340559..1c19d88 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 @@
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 432e77c..a76a328 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -192,5 +192,7 @@
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 7614999..8a1027b 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -33,8 +33,11 @@
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 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 6ac5cb8..49d2462 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.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 @@
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 @@
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 458daf1..3a89186 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 @@
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 @@
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 @@
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 c7adb60..83104e6 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 @@
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 @@
}
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 @@
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 93f72a8..f24e40b 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 @@
SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
registerPackageChangeReceivers();
+ createBatteryController();
mCarBatteryController.startListening();
mConnectedDeviceSignalController.startListening();
}
@@ -113,8 +114,7 @@
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 80ad9d2..9612db0 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 @@
}
}
- protected BatteryController createBatteryController() {
- return new BatteryControllerImpl(mContext);
- }
-
private void inflateShelf() {
mNotificationShelf =
(NotificationShelf) LayoutInflater.from(mContext).inflate(
@@ -1392,7 +1388,7 @@
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 c65f7150..cac0806 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 @@
// 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 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 @@
@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 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 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 @@
@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 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 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 @@
@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 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 @@
@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 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 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 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 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 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 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 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 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 88bc99f..96c468c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3311,6 +3311,16 @@
// 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 f718fa1..bee1f97 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -243,7 +243,6 @@
private PendingIntent mImeSwitchPendingIntent;
private boolean mShowOngoingImeSwitcherForPhones;
private boolean mNotificationShown;
- private final boolean mImeSelectedOnBoot;
static class SessionState {
final ClientState client;
@@ -566,7 +565,7 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
}
if (!mSystemReady) {
mSystemReady = true;
+ mLastSystemLocales = mRes.getConfiguration().getLocales();
final int currentUserId = mSettings.getCurrentUserId();
mSettings.switchCurrentUser(currentUserId,
!mUserManager.isUserUnlockingOrUnlocked(currentUserId));
@@ -1105,14 +1063,25 @@
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 @@
// 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 @@
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 94acd75..e11dd1a 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.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 @@
}
}
+ @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 df5f01d..49423b9 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 @@
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 0000000..4795fbf
--- /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 168884d..96459be 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 @@
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 @@
}
@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 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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 e8c3d97..2a5a25f 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.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 @@
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 @@
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
+ mChannel = channel;
mPreChannelsNotification = isPreChannelsNotification();
mSound = calculateSound();
mVibration = calculateVibration();
@@ -154,7 +156,7 @@
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 @@
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 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 c2cef09..492d5c6 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 @@
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 e44fb7f..1861bcb 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 7b32d20..63a5d14 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 @@
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 623a0a5..f3b0131 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 @@
}
/**
- * 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 @@
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 @@
}
/**
- * 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 @@
}
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 0000000..b26bac3
--- /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 ad436724a..468a26b 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 @@
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 f48d785..936531b 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 @@
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 eee9cf1..f8a32bb 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 @@
.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 403b65c..aa08b41 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 @@
.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 @@
.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 @@
.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 @@
|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 b6166f6..f0f4c4d 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 @@
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 @@
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 @@
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 @@
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 9b74fcc..92d9810 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 @@
.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 fc94271f..15dcc26 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 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 59ac427..0320d8a 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 @@
.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 @@
.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 @@
.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 @@
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 @@
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 @@
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 460fcdf..b7931d4 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 @@
.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 68765b6..6826975 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.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 @@
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 @@
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 @@
case AppOpsManager.MODE_ALLOWED:
return;
case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
return;
default: