Merge "Fix crash in SliceView" into pi-preview1-androidx-dev
diff --git a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index b4117e3..2c28cc8 100644
--- a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -526,71 +526,87 @@
}
@Test
- public void messagingStyle_isGroupConversation() {
+ public void testMessagingStyle_isGroupConversation() {
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_noConversationTitle() {
+ public void testMessagingStyle_isGroupConversation_noConversationTitle() {
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+ public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
// In legacy (version < P), isGroupConversation is controlled by conversationTitle.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+ public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
// In legacy (version < P), isGroupConversation is controlled by conversationTitle.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertFalse(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertFalse(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
+ public void testMessagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
// #setGroupConversation should always take precedence over legacy behavior, so a non-null
// title shouldn't affect #isGroupConversation.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -598,17 +614,21 @@
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(false)
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertFalse(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertFalse(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withoutConversationTitle_legacyWithOverride() {
+ public void testMessagingStyle_isGroupConversation_withoutTitle_legacyWithOverride() {
// #setGroupConversation should always take precedence over legacy behavior, so a null
// title shouldn't affect #isGroupConversation.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -616,13 +636,17 @@
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompat.java b/compat/src/main/java/androidx/core/app/NotificationCompat.java
index b82885f..b0da59b 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompat.java
@@ -2287,6 +2287,12 @@
@RestrictTo(LIBRARY_GROUP)
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
+ // This is called because we need to apply legacy logic before writing MessagingInfo
+ // data into the bundle. This does nothing in >= P, but in < P this will apply the
+ // correct group conversation status to new fields which will then be decoded properly
+ // by #extractMessagingStyleFromNotification.
+ setGroupConversation(isGroupConversation());
+
if (Build.VERSION.SDK_INT >= 24) {
Notification.MessagingStyle style =
new Notification.MessagingStyle(mUserDisplayName)
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
index 23a1afa..fa558c5 100644
--- a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
@@ -18,16 +18,17 @@
object Log {
- var currentLevel: LogLevel = LogLevel.INFO
+ var currentLevel: LogLevel = LogLevel.ERROR
var logConsumer: LogConsumer = StdOutLogConsumer()
fun setLevel(level: String?) {
currentLevel = when (level) {
+ "info" -> LogLevel.INFO
"error" -> LogLevel.ERROR
"debug" -> LogLevel.DEBUG
"verbose" -> LogLevel.VERBOSE
- else -> LogLevel.INFO
+ else -> LogLevel.ERROR
}
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
index 5e0e521..7c672d7 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.util.Consumer;
@@ -296,6 +297,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addLargeImage(@NonNull Icon image) {
return addImage(image, ListBuilder.LARGE_IMAGE, false /* isLoading */);
}
@@ -312,6 +314,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addLargeImage(@Nullable Icon image, boolean isLoading) {
return addImage(image, ListBuilder.LARGE_IMAGE, isLoading);
}
@@ -324,6 +327,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@NonNull Icon image) {
return addImage(image, ListBuilder.SMALL_IMAGE, false /* isLoading */);
}
@@ -333,6 +337,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@Nullable Icon image, boolean isLoading) {
return addImage(image, ListBuilder.SMALL_IMAGE, isLoading);
}
@@ -342,6 +347,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@NonNull Icon image, @ListBuilder.ImageMode int imageMode) {
return addImage(image, imageMode, false /* isLoading */);
}
@@ -351,6 +357,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@Nullable Icon image, @ListBuilder.ImageMode int imageMode,
boolean isLoading) {
mImpl.addImage(IconCompat.createFromIcon(image), imageMode, isLoading);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
index 612a4d7..c0b5bc3 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -28,6 +28,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.util.Consumer;
@@ -712,6 +713,7 @@
* @deprecated TO BE REMOVED
*/
@NonNull
+ @RequiresApi(23)
public InputRangeBuilder setThumb(@NonNull Icon thumb) {
return setThumb(IconCompat.createFromIcon(thumb));
}
@@ -846,6 +848,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@NonNull Icon icon) {
return setTitleItem(icon, ICON_IMAGE);
}
@@ -855,6 +858,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) {
return setTitleItem(icon, ICON_IMAGE, isLoading);
}
@@ -863,6 +867,7 @@
* @deprecated TO BE REMOVED.
*/
@Deprecated
+ @RequiresApi(23)
public RowBuilder setTitleItem(@NonNull Icon icon, @ImageMode int imageMode) {
mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, false /* isLoading */);
return this;
@@ -873,6 +878,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@Nullable Icon icon, @ImageMode int imageMode,
boolean isLoading) {
mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode,
@@ -1053,6 +1059,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon) {
return addEndItem(icon, ICON_IMAGE, false /* isLoading */);
}
@@ -1062,6 +1069,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) {
return addEndItem(icon, ICON_IMAGE, isLoading);
}
@@ -1071,6 +1079,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon, @ImageMode int imageMode) {
return addEndItem(icon, imageMode, false /* isLoading */);
}
@@ -1080,6 +1089,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@Nullable Icon icon, @ImageMode int imageMode,
boolean isLoading) {
if (mHasEndActionOrToggle) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
index 7dafb35..6fb22ef 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
@@ -120,7 +121,9 @@
* Add the icon used to display contact in the messaging experience
*/
public MessageBuilder addSource(IconCompat source) {
- mImpl.addSource(source.toIcon());
+ if (Build.VERSION.SDK_INT >= 23) {
+ mImpl.addSource(source.toIcon());
+ }
return this;
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
index e4e28f5..b98c723 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
@@ -25,6 +25,7 @@
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -41,6 +42,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@NonNull CharSequence actionTitle) {
this(action, actionIcon, ICON_IMAGE, actionTitle);
@@ -50,6 +52,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@ListBuilder.ImageMode int imageMode, @NonNull CharSequence actionTitle) {
this(action, IconCompat.createFromIcon(actionIcon), imageMode, actionTitle);
@@ -59,6 +62,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@NonNull CharSequence actionTitle, boolean isChecked) {
this(action, IconCompat.createFromIcon(actionIcon), actionTitle, isChecked);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
index c999594..f53b9f5 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
@@ -19,7 +19,9 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import android.graphics.drawable.Icon;
+import android.os.Build;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -44,8 +46,10 @@
@Override
public void apply(Slice.Builder builder) {
if (mLastMessage != null) {
- if (mLastMessage.mIcon != null) {
- builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (mLastMessage.mIcon != null) {
+ builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+ }
}
if (mLastMessage.mText != null) {
builder.addText(mLastMessage.mText, null);
@@ -75,6 +79,7 @@
public static final class MessageBuilder extends TemplateBuilderImpl
implements MessagingBuilder.MessageBuilder {
+ @RequiresApi(23)
private Icon mIcon;
private CharSequence mText;
private long mTimestamp;
@@ -94,6 +99,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
mIcon = source;
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
index dd66616..a9fb0cc 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
/**
@@ -44,6 +45,7 @@
/**
* Add the icon used to display contact in the messaging experience
*/
+ @RequiresApi(23)
void addSource(Icon source);
/**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
index e47871a..99f0f34 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
@@ -22,6 +22,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -85,6 +86,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
mListBuilder.setTitleItem(IconCompat.createFromIcon(source), SMALL_IMAGE);
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
index e601491..63214da 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -71,6 +72,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
getBuilder().addIcon(IconCompat.createFromIcon(source),
android.app.slice.Slice.SUBTYPE_SOURCE);
diff --git a/slices/core/src/main/java/androidx/slice/Slice.java b/slices/core/src/main/java/androidx/slice/Slice.java
index ac571f7..c6ced6d 100644
--- a/slices/core/src/main/java/androidx/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/slice/Slice.java
@@ -39,6 +39,7 @@
import static androidx.slice.SliceConvert.unwrap;
import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
import static androidx.slice.core.SliceHints.HINT_TTL;
import android.app.PendingIntent;
@@ -84,9 +85,24 @@
* @hide
*/
@RestrictTo(Scope.LIBRARY)
- @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
- HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE,
- HINT_SHORTCUT, HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED})
+ @StringDef({
+ HINT_TITLE,
+ HINT_LIST,
+ HINT_LIST_ITEM,
+ HINT_LARGE,
+ HINT_ACTIONS,
+ HINT_SELECTED,
+ HINT_HORIZONTAL,
+ HINT_NO_TINT,
+ HINT_PARTIAL,
+ HINT_SUMMARY,
+ HINT_SEE_MORE,
+ HINT_SHORTCUT,
+ HINT_KEYWORDS,
+ HINT_TTL,
+ HINT_LAST_UPDATED,
+ HINT_PERMISSION_REQUEST,
+ })
public @interface SliceHint{ }
private final SliceItem[] mItems;
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
index 5e8eed0..182b3c2 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
@@ -15,9 +15,12 @@
*/
package androidx.slice.compat;
-import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.SliceProvider.SLICE_TYPE;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
+
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -175,7 +178,7 @@
b.putParcelable(EXTRA_SLICE, null);
}
return b;
- } else if (method.equals(METHOD_MAP_INTENT)) {
+ } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
Intent intent = extras.getParcelable(EXTRA_INTENT);
Uri uri = mSliceProvider.onMapIntentToUri(intent);
Bundle b = new Bundle();
@@ -249,13 +252,19 @@
*/
public static Slice createPermissionSlice(Context context, Uri sliceUri,
String callingPackage) {
- return new Slice.Builder(sliceUri)
+ Slice.Builder parent = new Slice.Builder(sliceUri);
+
+ Slice.Builder action = new Slice.Builder(parent)
+ .addHints(HINT_TITLE, HINT_SHORTCUT)
.addAction(createPermissionIntent(context, sliceUri, callingPackage),
- new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
- .addText(getPermissionString(context, callingPackage), null)
- .build(), null)
- .addHints(HINT_LIST_ITEM)
- .build();
+ new Slice.Builder(parent).build(), null);
+
+ parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+ .addText(getPermissionString(context, callingPackage), null)
+ .addSubSlice(action.build())
+ .build());
+
+ return parent.addHints(HINT_PERMISSION_REQUEST).build();
}
/**
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
index 1c13f94..3fb9ada 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
@@ -78,6 +78,8 @@
*/
public static final String SUBTYPE_MILLIS = "millis";
+ public static final String HINT_PERMISSION_REQUEST = "permission_request";
+
@IntDef({
LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
})
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index caa2692..0e1c759 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -28,6 +28,7 @@
method public java.util.List<java.lang.String> getSliceKeywords();
method public java.util.List<androidx.slice.core.SliceAction> getToggles();
method public boolean hasLargeMode();
+ method public boolean isPermissionSlice();
field public static final int LOADED_ALL = 2; // 0x2
field public static final int LOADED_NONE = 0; // 0x0
field public static final int LOADED_PARTIAL = 1; // 0x1
diff --git a/slices/view/src/androidTest/AndroidManifest.xml b/slices/view/src/androidTest/AndroidManifest.xml
index 3bb50d5..78f3ad8 100644
--- a/slices/view/src/androidTest/AndroidManifest.xml
+++ b/slices/view/src/androidTest/AndroidManifest.xml
@@ -20,10 +20,15 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <application>
+ <application
+ android:label="Slice Render Test">
<provider android:name="androidx.slice.SliceManagerTest$TestSliceProvider"
android:authorities="androidx.slice.view.test"
- android:exported="true"/>
+ android:exported="true">
+ <intent-filter>
+ <action android:name="androidx.slice.action.TEST" />
+ </intent-filter>
+ </provider>
<activity android:name="androidx.slice.render.SliceRenderActivity"
android:theme="@style/AppTheme.NoActionBar">
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
index 52e6ef6..2c6731d 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
@@ -30,11 +30,14 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import androidx.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+
+import androidx.annotation.NonNull;
import androidx.core.os.BuildCompat;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.SliceLiveData;
import org.junit.Before;
import org.junit.Test;
@@ -42,8 +45,6 @@
import java.util.concurrent.Executor;
-import androidx.slice.widget.SliceLiveData;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SliceManagerTest {
@@ -124,14 +125,27 @@
assertEquals(SliceLiveData.SUPPORTED_SPECS, mManager.getPinnedSpecs(uri));
}
- //@Test
- //public void testMapIntentToUri() {
- // Uri expected = Uri.parse("content://androidx.slice.view.test/render");
- // Slice s = new Slice.Builder(expected).build();
- // when(mSliceProvider.onBindSlice(eq(expected))).thenReturn(s);
- // Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
- // assertEquals(expected, uri);
- //}
+ @Test
+ public void testMapIntentToUriStatic() {
+ Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+
+ Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+
+ assertEquals(expected, uri);
+ }
+
+ @Test
+ public void testMapIntentToUri() {
+ Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+ Intent intent = new Intent("androidx.slice.action.TEST")
+ .setPackage(mContext.getPackageName());
+
+ when(mSliceProvider.onMapIntentToUri(eq(intent))).thenReturn(expected);
+ Uri uri = mManager.mapIntentToUri(intent);
+
+ assertEquals(expected, uri);
+ verify(mSliceProvider).onMapIntentToUri(eq(intent));
+ }
public static class TestSliceProvider extends SliceProvider {
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
index 32a01db..d378e2e 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
@@ -47,6 +47,7 @@
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
+import androidx.slice.compat.SliceProviderCompat;
import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceHints;
import androidx.slice.render.SliceRenderActivity;
@@ -588,6 +589,16 @@
assertEquals(0, retrievedLastUpdated2);
}
+ @Test
+ public void testIsPermissionSlice() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ Slice permissionSlice =
+ SliceProviderCompat.createPermissionSlice(mContext, uri, mContext.getPackageName());
+
+ SliceMetadata metadata = SliceMetadata.from(mContext, permissionSlice);
+ assertEquals(true, metadata.isPermissionSlice());
+ }
+
private PendingIntent getIntent(String action) {
Intent intent = new Intent(action);
intent.setClassName(mContext.getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
index 4873382..e0bccfd 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
@@ -40,6 +40,7 @@
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.MessagingSliceBuilder;
import androidx.slice.builders.SliceAction;
+import androidx.slice.compat.SliceProviderCompat;
import androidx.slice.view.test.R;
import java.util.Arrays;
@@ -58,7 +59,7 @@
public static final String[] URI_PATHS = {"message", "wifi", "wifi2", "note", "ride",
"ride-ttl", "toggle", "toggle2", "contact", "gallery", "subscription", "subscription2",
- "weather", "reservation", "inputrange", "range"};
+ "weather", "reservation", "inputrange", "range", "permission"};
private final Context mContext;
@@ -116,6 +117,8 @@
return createStarRatingInputRange(sliceUri);
case "/range":
return createDownloadProgressRange(sliceUri);
+ case "/permission":
+ return createPermissionSlice(sliceUri);
}
throw new IllegalArgumentException("Unknown uri " + sliceUri);
}
@@ -472,11 +475,11 @@
.build();
}
-
private Slice createStarRatingInputRange(Uri sliceUri) {
IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
SliceAction primaryAction =
- new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon, "Rate");
+ new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon,
+ "Rate");
ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
return lb.setColor(0xffff4081)
.addInputRange(new ListBuilder.InputRangeBuilder(lb)
@@ -495,7 +498,8 @@
IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
SliceAction primaryAction =
new SliceAction(
- getBroadcastIntent(ACTION_TOAST, "open download"), icon, "Download");
+ getBroadcastIntent(ACTION_TOAST, "open download"), icon,
+ "Download");
ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
return lb.setColor(0xffff4081)
.addRange(new ListBuilder.RangeBuilder(lb)
@@ -507,6 +511,11 @@
.build();
}
+ private Slice createPermissionSlice(Uri uri) {
+ return SliceProviderCompat.createPermissionSlice(getContext(), uri,
+ getContext().getPackageName());
+ }
+
private PendingIntent getIntent(String action) {
Intent intent = new Intent(action);
intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/main/java/androidx/slice/SliceMetadata.java b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
index db5f7ae..9a4648f 100644
--- a/slices/view/src/main/java/androidx/slice/SliceMetadata.java
+++ b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
@@ -27,6 +27,7 @@
import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
import static androidx.slice.core.SliceHints.HINT_TTL;
import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
@@ -279,6 +280,18 @@
}
/**
+ * To present a slice from another app, the app must grant uri permissions for the slice. If
+ * these permissions have not been granted and the app slice is requested then
+ * a permission request slice will be returned instead, allowing the user to grant permission.
+ * This method can be used to identify if a slice is a permission request.
+ *
+ * @return whether this slice represents a permission request.
+ */
+ public boolean isPermissionSlice() {
+ return mSlice.hasHint(HINT_PERMISSION_REQUEST);
+ }
+
+ /**
* @return the group of actions associated with the provided slice, if they exist.
* @hide
*/
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index b151437..48f7c81 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -532,7 +532,7 @@
long expiry = sliceMetadata.getExpiry();
long now = System.currentTimeMillis();
mCurrentView.setLastUpdated(lastUpdated);
- boolean expired = expiry != SliceHints.INFINITY && now > expiry;
+ boolean expired = expiry != 0 && expiry != SliceHints.INFINITY && now > expiry;
mCurrentView.setShowLastUpdated(mShowLastUpdated && expired);
// Set the slice