Merge "Fix .equals call in ImageDecoder" into rvc-dev
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 5c43f8f..08d9905 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -486,15 +486,8 @@
/**
* @hide
*/
- public String getShortcutId(Context context) {
- String conversationId = getNotification().getShortcutId();
- if (TextUtils.isEmpty(conversationId)
- && (Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0)
- && getNotification().getNotificationStyle() == Notification.MessagingStyle.class) {
- conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID;
- }
- return conversationId;
+ public String getShortcutId() {
+ return getNotification().getShortcutId();
}
/**
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index bcb32fb..a47ad73 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -301,7 +301,7 @@
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
- activeListAdapter.postListReadyRunnable(doPostProcessing);
+ activeListAdapter.postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true);
return false;
}
return activeListAdapter.rebuildList(doPostProcessing);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index b671fa7..a84d964 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2404,10 +2404,9 @@
public ChooserGridAdapter createChooserGridAdapter(Context context,
List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
- ChooserListAdapter chooserListAdapter = new ChooserListAdapter(context, payloadIntents,
- initialIntents, rList,
- filterLastUsed, createListController(userHandle), useLayoutForBrowsables,
- this, this);
+ ChooserListAdapter chooserListAdapter = createChooserListAdapter(context, payloadIntents,
+ initialIntents, rList, filterLastUsed,
+ useLayoutForBrowsables, createListController(userHandle));
AppPredictor.Callback appPredictorCallback = createAppPredictorCallback(chooserListAdapter);
AppPredictor appPredictor = setupAppPredictorForUser(userHandle, appPredictorCallback);
chooserListAdapter.setAppPredictor(appPredictor);
@@ -2416,6 +2415,16 @@
}
@VisibleForTesting
+ public ChooserListAdapter createChooserListAdapter(Context context,
+ List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+ boolean filterLastUsed, boolean useLayoutForBrowsables,
+ ResolverListController resolverListController) {
+ return new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
+ filterLastUsed, resolverListController, useLayoutForBrowsables,
+ this, this, context.getPackageManager());
+ }
+
+ @VisibleForTesting
protected ResolverListController createListController(UserHandle userHandle) {
AppPredictor appPredictor = getAppPredictorForShareActivitiesIfEnabled(userHandle);
AbstractResolverComparator resolverComparator;
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 69a4927..73ee295 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -88,7 +88,7 @@
private final int mMaxShortcutTargetsPerApp;
private final ChooserListCommunicator mChooserListCommunicator;
private final SelectableTargetInfo.SelectableTargetInfoCommunicator
- mSelectableTargetInfoComunicator;
+ mSelectableTargetInfoCommunicator;
private int mNumShortcutResults = 0;
@@ -117,7 +117,8 @@
boolean filterLastUsed, ResolverListController resolverListController,
boolean useLayoutForBrowsables,
ChooserListCommunicator chooserListCommunicator,
- SelectableTargetInfo.SelectableTargetInfoCommunicator selectableTargetInfoComunicator) {
+ SelectableTargetInfo.SelectableTargetInfoCommunicator selectableTargetInfoCommunicator,
+ PackageManager packageManager) {
// Don't send the initial intents through the shared ResolverActivity path,
// we want to separate them into a different section.
super(context, payloadIntents, null, rList, filterLastUsed,
@@ -128,10 +129,9 @@
mMaxShortcutTargetsPerApp =
context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp);
mChooserListCommunicator = chooserListCommunicator;
- mSelectableTargetInfoComunicator = selectableTargetInfoComunicator;
+ mSelectableTargetInfoCommunicator = selectableTargetInfoCommunicator;
if (initialIntents != null) {
- final PackageManager pm = context.getPackageManager();
for (int i = 0; i < initialIntents.length; i++) {
final Intent ii = initialIntents[i];
if (ii == null) {
@@ -147,7 +147,7 @@
final ComponentName cn = ii.getComponent();
if (cn != null) {
try {
- ai = pm.getActivityInfo(ii.getComponent(), 0);
+ ai = packageManager.getActivityInfo(ii.getComponent(), 0);
ri = new ResolveInfo();
ri.activityInfo = ai;
} catch (PackageManager.NameNotFoundException ignored) {
@@ -155,7 +155,7 @@
}
}
if (ai == null) {
- ri = pm.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY);
+ ri = packageManager.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY);
ai = ri != null ? ri.activityInfo : null;
}
if (ai == null) {
@@ -455,7 +455,7 @@
UserHandle userHandle = getUserHandle();
Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */);
boolean isInserted = insertServiceTarget(new SelectableTargetInfo(contextAsUser,
- origTarget, target, targetScore, mSelectableTargetInfoComunicator,
+ origTarget, target, targetScore, mSelectableTargetInfoCommunicator,
(isShortcutResult ? directShareToShortcutInfos.get(target) : null)));
if (isInserted && isShortcutResult) {
@@ -507,7 +507,7 @@
.map(target ->
new SelectableTargetInfo(
contextAsUser, origTarget, target, target.getScore(),
- mSelectableTargetInfoComunicator,
+ mSelectableTargetInfoCommunicator,
(isShortcutResult ? directShareToShortcutInfos.get(target)
: null))
)
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 4ba6ce4..cf9b955 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1026,7 +1026,8 @@
}
@Override // ResolverListCommunicator
- public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
+ public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
+ boolean rebuildCompleted) {
if (isAutolaunching()) {
return;
}
@@ -1041,7 +1042,7 @@
}
// showEmptyResolverListEmptyState can mark the tab as loaded,
// which is a precondition for auto launching
- if (maybeAutolaunchActivity()) {
+ if (rebuildCompleted && maybeAutolaunchActivity()) {
return;
}
if (doPostProcessing) {
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 62bddb1..2fd938f 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -282,7 +282,7 @@
}
setPlaceholderCount(placeholderCount);
createSortingTask(doPostProcessing).execute(currentResolveList);
- postListReadyRunnable(doPostProcessing);
+ postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ false);
return false;
} else {
processSortedList(currentResolveList, doPostProcessing);
@@ -370,7 +370,7 @@
}
mResolverListCommunicator.sendVoiceChoicesIfNeeded();
- postListReadyRunnable(doPostProcessing);
+ postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true);
mIsTabLoaded = true;
}
@@ -380,14 +380,15 @@
* handler thread to update after the current task is finished.
* @param doPostProcessing Whether to update the UI and load additional direct share targets
* after the list has been rebuilt
+ * @param rebuildCompleted Whether the list has been completely rebuilt
*/
- void postListReadyRunnable(boolean doPostProcessing) {
+ void postListReadyRunnable(boolean doPostProcessing, boolean rebuildCompleted) {
if (mPostListReadyRunnable == null) {
mPostListReadyRunnable = new Runnable() {
@Override
public void run() {
mResolverListCommunicator.onPostListReady(ResolverListAdapter.this,
- doPostProcessing);
+ doPostProcessing, rebuildCompleted);
mPostListReadyRunnable = null;
}
};
@@ -649,7 +650,8 @@
Intent getReplacementIntent(ActivityInfo activityInfo, Intent defIntent);
- void onPostListReady(ResolverListAdapter listAdapter, boolean updateUi);
+ void onPostListReady(ResolverListAdapter listAdapter, boolean updateUi,
+ boolean rebuildCompleted);
void sendVoiceChoicesIfNeeded();
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c52555b..edad5c4 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,6 +34,7 @@
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static com.android.internal.app.MatcherUtils.first;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.is;
@@ -41,6 +42,8 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
@@ -55,6 +58,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -302,7 +307,7 @@
waitForIdle();
UsageStatsManager usm = activity.getUsageStatsManager();
verify(sOverrides.resolverListController, times(1))
- .topK(Mockito.any(List.class), Mockito.anyInt());
+ .topK(any(List.class), anyInt());
assertThat(activity.getIsSelected(), is(false));
sOverrides.onSafelyStartCallback = targetInfo -> {
return true;
@@ -312,7 +317,7 @@
.perform(click());
waitForIdle();
verify(sOverrides.resolverListController, times(1))
- .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString());
+ .updateChooserCounts(Mockito.anyString(), anyInt(), Mockito.anyString());
verify(sOverrides.resolverListController, times(1))
.updateModel(toChoose.activityInfo.getComponentName());
assertThat(activity.getIsSelected(), is(true));
@@ -1750,6 +1755,54 @@
assertThat(chosen[0], is(personalResolvedComponentInfos.get(1).getResolveInfoAt(0)));
}
+ @Test
+ public void testOneInitialIntent_noAutolaunch() {
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(1);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ Intent chooserIntent = createChooserIntent(new Intent[] {new Intent("action.fake")});
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+ sOverrides.packageManager = mock(PackageManager.class);
+ ResolveInfo ri = createFakeResolveInfo();
+ when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(ri);
+ waitForIdle();
+
+ mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+
+ assertNull(chosen[0]);
+ }
+
+ private Intent createChooserIntent(Intent[] initialIntents) {
+ Intent chooserIntent = new Intent();
+ chooserIntent.setAction(Intent.ACTION_CHOOSER);
+ chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title");
+ chooserIntent.putExtra(Intent.EXTRA_INTENT, createSendTextIntent());
+ chooserIntent.setType("text/plain");
+ if (initialIntents != null) {
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents);
+ }
+ return chooserIntent;
+ }
+
+ private ResolveInfo createFakeResolveInfo() {
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.name = "FakeActivityName";
+ ri.activityInfo.packageName = "fake.package.name";
+ ri.activityInfo.applicationInfo = new ApplicationInfo();
+ ri.activityInfo.applicationInfo.packageName = "fake.package.name";
+ return ri;
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 5b83f95..071b225 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -58,6 +58,18 @@
return multiProfilePagerAdapter;
}
+ @Override
+ public ChooserListAdapter createChooserListAdapter(Context context, List<Intent> payloadIntents,
+ Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
+ boolean useLayoutForBrowsables, ResolverListController resolverListController) {
+ PackageManager packageManager =
+ sOverrides.packageManager == null ? context.getPackageManager()
+ : sOverrides.packageManager;
+ return new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
+ filterLastUsed, resolverListController, useLayoutForBrowsables,
+ this, this, packageManager);
+ }
+
ChooserListAdapter getAdapter() {
return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
}
@@ -217,6 +229,7 @@
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
+ public PackageManager packageManager;
public void reset() {
onSafelyStartCallback = null;
@@ -235,6 +248,7 @@
workProfileUserHandle = null;
hasCrossProfileIntents = true;
isQuietModeEnabled = false;
+ packageManager = null;
multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
@Override
public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index db109fe..94a6bc5 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -38,6 +38,7 @@
android:elevation="1dp"
android:fillViewport="true"
android:layout_marginHorizontal="@dimen/screenshot_action_container_margin_horizontal"
+ android:layout_marginBottom="@dimen/screenshot_action_container_offset_y"
android:gravity="center"
android:paddingLeft="@dimen/screenshot_action_container_padding_left"
android:paddingRight="@dimen/screenshot_action_container_padding_right"
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 6b94bef..bd91ddb 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -16,14 +16,15 @@
-->
<com.android.systemui.screenshot.ScreenshotActionChip
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_screenshot_action_chip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="@dimen/screenshot_action_chip_margin_right"
- android:layout_gravity="center"
- android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
- android:background="@drawable/action_chip_background"
- android:gravity="center">
+ android:id="@+id/global_screenshot_action_chip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/screenshot_action_chip_margin_right"
+ android:layout_gravity="center"
+ android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
+ android:background="@drawable/action_chip_background"
+ android:alpha="0"
+ android:gravity="center">
<ImageView
android:id="@+id/screenshot_action_chip_icon"
android:layout_width="@dimen/screenshot_action_chip_icon_size"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
new file mode 100644
index 0000000..2401dfb
--- /dev/null
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020, 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.
+-->
+
+<com.android.systemui.statusbar.notification.row.PartialConversationInfo
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_guts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clipChildren="false"
+ android:clipToPadding="true"
+ android:orientation="vertical"
+ android:paddingStart="@*android:dimen/notification_content_margin_start">
+
+ <!-- Package Info -->
+ <LinearLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_guts_conversation_header_height"
+ android:gravity="center_vertical"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <ImageView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/notification_guts_conversation_icon_size"
+ android:layout_height="@dimen/notification_guts_conversation_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginEnd="15dp" />
+ <LinearLayout
+ android:id="@+id/names"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_guts_conversation_icon_size"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ android:layout_alignEnd="@id/conversation_icon"
+ android:layout_toEndOf="@id/conversation_icon">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ style="@style/TextAppearance.NotificationImportanceHeader"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:text="@*android:string/notification_header_divider_symbol" />
+ <TextView
+ android:id="@+id/parent_channel_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
+
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/pkg_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"
+ android:ellipsize="end"
+ android:maxLines="1"/>
+ <TextView
+ android:id="@+id/group_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ style="@style/TextAppearance.NotificationImportanceHeader"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:text="@*android:string/notification_header_divider_symbol" />
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/delegate_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ style="@style/TextAppearance.NotificationImportanceHeader"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:ellipsize="end"
+ android:text="@string/notification_delegate_header"
+ android:maxLines="1" />
+
+ </LinearLayout>
+
+ <!-- end aligned fields -->
+ <ImageButton
+ android:id="@+id/info"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:layout_centerVertical="true"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_more_settings"
+ android:src="@drawable/ic_settings"
+ android:layout_alignParentEnd="true"
+ android:tint="@color/notification_guts_link_icon_tint"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/inline_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_guts_option_vertical_padding"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_info"
+ android:tint="?android:attr/textColorPrimary"
+ android:layout_marginEnd="8dp"/>
+ <TextView
+ android:id="@+id/non_configurable_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:id="@+id/bottom_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:gravity="center_vertical"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ >
+ <TextView
+ android:id="@+id/turn_off_notifications"
+ android:text="@string/inline_turn_off_notifications"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/done"
+ android:text="@string/inline_done_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="end|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
+
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.PartialConversationInfo>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9f0f482..43ebb40 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1847,6 +1847,9 @@
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
<string name="notification_priority_title">Priority</string>
+ <!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
+ <string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> does not support conversation specific settings</string>
+
<!-- [CHAR LIMIT=NONE] Empty overflow title -->
<string name="bubble_overflow_empty_title">No recent bubbles</string>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 290816b..fdf2c34 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -82,6 +82,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@@ -162,6 +163,9 @@
private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
+ private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
+ private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
+ private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
private static final float ROUNDED_CORNER_RADIUS = .05f;
private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
private static final int MESSAGE_CORNER_TIMEOUT = 2;
@@ -263,6 +267,7 @@
mScreenshotSelectorView.setFocusableInTouchMode(true);
mScreenshotView.setPivotX(0);
mScreenshotView.setPivotY(0);
+ mActionsContainer.setPivotX(0);
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -661,6 +666,8 @@
} catch (RemoteException e) {
}
+ ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
+
for (Notification.Action smartAction : imageData.smartActions) {
ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
@@ -673,6 +680,7 @@
mOnCompleteRunnable.run();
});
mActionsView.addView(actionChip);
+ chips.add(actionChip);
}
ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate(
@@ -685,6 +693,7 @@
mOnCompleteRunnable.run();
});
mActionsView.addView(shareChip);
+ chips.add(shareChip);
ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
@@ -696,6 +705,7 @@
mOnCompleteRunnable.run();
});
mActionsView.addView(editChip);
+ chips.add(editChip);
mScreenshotView.setOnClickListener(v -> {
try {
@@ -709,7 +719,6 @@
});
mScreenshotView.setContentDescription(imageData.editAction.title);
-
if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) {
ScreenshotActionChip scrollChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
@@ -723,18 +732,27 @@
scrollNotImplemented.show();
});
mActionsView.addView(scrollChip);
+ chips.add(scrollChip);
}
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
- mActionsContainer.setY(mDisplayMetrics.heightPixels);
+ animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
+ float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
+ / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
mActionsContainer.setVisibility(VISIBLE);
- mActionsContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- float actionsViewHeight = mActionsContainer.getMeasuredHeight() + mScreenshotHeightPx;
+ mActionsContainer.setAlpha(0);
animator.addUpdateListener(animation -> {
float t = animation.getAnimatedFraction();
mBackgroundProtection.setAlpha(t);
- mActionsContainer.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
+ mActionsContainer.setAlpha(t < alphaFraction ? t / alphaFraction : 1);
+ float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X
+ + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
+ mActionsContainer.setScaleX(containerScale);
+ for (ScreenshotActionChip chip : chips) {
+ chip.setAlpha(t);
+ chip.setScaleX(1 / containerScale); // invert to keep size of children constant
+ }
});
return animator;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
index ff945d1..1c2a00e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
@@ -26,7 +26,6 @@
import android.text.TextUtils;
import android.util.Slog;
-import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
@@ -44,32 +43,18 @@
if (!TextUtils.isEmpty(channel.getConversationId())) {
return channel;
}
- final String conversationId = entry.getSbn().getShortcutId(context);
+ final String conversationId = entry.getSbn().getShortcutId();
final String pkg = entry.getSbn().getPackageName();
final int appUid = entry.getSbn().getUid();
- if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg)) {
+ if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg)
+ || entry.getRanking().getShortcutInfo() == null) {
return channel;
}
- String name;
- if (entry.getRanking().getShortcutInfo() != null) {
- name = entry.getRanking().getShortcutInfo().getShortLabel().toString();
- } else {
- Bundle extras = entry.getSbn().getNotification().extras;
- String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
- if (TextUtils.isEmpty(nameString)) {
- nameString = extras.getString(Notification.EXTRA_TITLE);
- }
- name = nameString;
- }
-
// If this channel is not already a customized conversation channel, create
// a custom channel
try {
- // TODO: When shortcuts are enforced remove this and use the shortcut label for naming
- channel.setName(context.getString(
- R.string.notification_summary_message_format,
- name, channel.getName()));
+ channel.setName(getName(entry));
notificationManager.createConversationNotificationChannelForPackage(
pkg, appUid, entry.getSbn().getKey(), channel,
conversationId);
@@ -81,4 +66,19 @@
}
return channel;
}
+
+ private static String getName(NotificationEntry entry) {
+ if (entry.getRanking().getShortcutInfo().getShortLabel() != null) {
+ return entry.getRanking().getShortcutInfo().getShortLabel().toString();
+ }
+ Bundle extras = entry.getSbn().getNotification().extras;
+ String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
+ if (TextUtils.isEmpty(nameString)) {
+ nameString = extras.getString(Notification.EXTRA_TITLE);
+ }
+ if (TextUtils.isEmpty(nameString)) {
+ nameString = "fallback";
+ }
+ return nameString;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a9bb416..66b2ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1141,6 +1141,7 @@
if (mMenuRow.shouldUseDefaultMenuItems()) {
ArrayList<MenuItem> items = new ArrayList<>();
items.add(NotificationMenuRow.createConversationItem(mContext));
+ items.add(NotificationMenuRow.createPartialConversationItem(mContext));
items.add(NotificationMenuRow.createInfoItem(mContext));
items.add(NotificationMenuRow.createSnoozeItem(mContext));
items.add(NotificationMenuRow.createAppOpsItem(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index f23f3bf..e9849ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1347,11 +1347,11 @@
if (bubbleButton == null || actionContainer == null) {
return;
}
- boolean isPerson =
+ boolean isPersonWithShortcut =
mPeopleIdentifier.getPeopleNotificationType(entry.getSbn(), entry.getRanking())
- != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+ >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
boolean showButton = isBubblesEnabled()
- && isPerson
+ && isPersonWithShortcut
&& entry.getBubbleMetadata() != null;
if (showButton) {
Drawable d = mContext.getResources().getDrawable(entry.isBubble()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 23b911b..863951e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -97,7 +97,6 @@
private String mDelegatePkg;
private NotificationChannel mNotificationChannel;
private ShortcutInfo mShortcutInfo;
- private String mConversationId;
private StatusBarNotification mSbn;
@Nullable private Notification.BubbleMetadata mBubbleMetadata;
private Context mUserContext;
@@ -233,14 +232,10 @@
mBuilderProvider = builderProvider;
mShortcutManager = shortcutManager;
- mConversationId = mNotificationChannel.getConversationId();
- if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
- mConversationId = mSbn.getShortcutId(mContext);
- }
- if (TextUtils.isEmpty(mConversationId)) {
+ mShortcutInfo = entry.getRanking().getShortcutInfo();
+ if (mShortcutInfo == null) {
throw new IllegalArgumentException("Does not have required information");
}
- mShortcutInfo = entry.getRanking().getShortcutInfo();
mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
getContext(), mINotificationManager, entry, mNotificationChannel);
@@ -319,31 +314,9 @@
private void bindIcon(boolean important) {
ImageView image = findViewById(R.id.conversation_icon);
- if (mShortcutInfo != null) {
- image.setImageDrawable(mIconFactory.getConversationDrawable(
- mShortcutInfo, mPackageName, mAppUid,
- important));
- } else {
- if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
- // TODO: maybe use a generic group icon, or a composite of recent senders
- image.setImageDrawable(mPm.getDefaultActivityIcon());
- } else {
- final List<Notification.MessagingStyle.Message> messages =
- Notification.MessagingStyle.Message.getMessagesFromBundleArray(
- (Parcelable[]) mSbn.getNotification().extras.get(
- Notification.EXTRA_MESSAGES));
+ image.setImageDrawable(mIconFactory.getConversationDrawable(
+ mShortcutInfo, mPackageName, mAppUid, important));
- final Notification.MessagingStyle.Message latestMessage =
- Notification.MessagingStyle.findLatestIncomingMessage(messages);
- Icon personIcon = latestMessage.getSenderPerson().getIcon();
- if (personIcon != null) {
- image.setImageIcon(latestMessage.getSenderPerson().getIcon());
- } else {
- // TODO: choose something better
- image.setImageDrawable(mPm.getDefaultActivityIcon());
- }
- }
- }
}
private void bindPackage() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 1c808cf9..9c7de2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -252,6 +252,9 @@
} else if (gutsView instanceof NotificationConversationInfo) {
initializeConversationNotificationInfo(
row, (NotificationConversationInfo) gutsView);
+ } else if (gutsView instanceof PartialConversationInfo) {
+ initializePartialConversationNotificationInfo(row,
+ (PartialConversationInfo) gutsView);
}
return true;
} catch (Exception e) {
@@ -357,7 +360,47 @@
}
/**
- * Sets up the {@link NotificationConversationInfo} inside the notification row's guts.
+ * Sets up the {@link PartialConversationInfo} inside the notification row's guts.
+ * @param row view to set up the guts for
+ * @param notificationInfoView view to set up/bind within {@code row}
+ */
+ @VisibleForTesting
+ void initializePartialConversationNotificationInfo(
+ final ExpandableNotificationRow row,
+ PartialConversationInfo notificationInfoView) throws Exception {
+ NotificationGuts guts = row.getGuts();
+ StatusBarNotification sbn = row.getEntry().getSbn();
+ String packageName = sbn.getPackageName();
+ // Settings link is only valid for notifications that specify a non-system user
+ NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+ UserHandle userHandle = sbn.getUser();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ mContext, userHandle.getIdentifier());
+
+ if (!userHandle.equals(UserHandle.ALL)
+ || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+ onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
+ mOnSettingsClickListener.onSettingsClick(sbn.getKey());
+ startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+ };
+ }
+
+ notificationInfoView.bindNotification(
+ pmUser,
+ mNotificationManager,
+ packageName,
+ row.getEntry().getChannel(),
+ row.getUniqueChannels(),
+ row.getEntry(),
+ onSettingsClick,
+ mDeviceProvisionedController.isDeviceProvisioned(),
+ row.getIsNonblockable());
+ }
+
+ /**
+ * Sets up the {@link ConversationInfo} inside the notification row's guts.
* @param row view to set up the guts for
* @param notificationInfoView view to set up/bind within {@code row}
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 83a6eb2..5e1e3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -268,7 +268,9 @@
NotificationEntry entry = mParent.getEntry();
int personNotifType = mPeopleNotificationIdentifier
.getPeopleNotificationType(entry.getSbn(), entry.getRanking());
- if (personNotifType != PeopleNotificationIdentifier.TYPE_NON_PERSON) {
+ if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) {
+ mInfoItem = createPartialConversationItem(mContext);
+ } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
mInfoItem = createConversationItem(mContext);
} else {
mInfoItem = createInfoItem(mContext);
@@ -667,6 +669,16 @@
R.drawable.ic_settings);
}
+ static NotificationMenuItem createPartialConversationItem(Context context) {
+ Resources res = context.getResources();
+ String infoDescription = res.getString(R.string.notification_menu_gear_description);
+ PartialConversationInfo infoContent =
+ (PartialConversationInfo) LayoutInflater.from(context).inflate(
+ R.layout.partial_conversation_info, null, false);
+ return new NotificationMenuItem(context, infoDescription, infoContent,
+ R.drawable.ic_settings);
+ }
+
static NotificationMenuItem createInfoItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
new file mode 100644
index 0000000..2189b87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.transition.ChangeBounds;
+import android.transition.Fade;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.lang.annotation.Retention;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The guts of a conversation notification that doesn't use valid shortcuts that is revealed when
+ * performing a long press.
+ */
+public class PartialConversationInfo extends LinearLayout implements
+ NotificationGuts.GutsContent {
+ private static final String TAG = "PartialConvoGuts";
+
+ private INotificationManager mINotificationManager;
+ private PackageManager mPm;
+ private String mPackageName;
+ private String mAppName;
+ private int mAppUid;
+ private String mDelegatePkg;
+ private NotificationChannel mNotificationChannel;
+ private StatusBarNotification mSbn;
+ private boolean mIsDeviceProvisioned;
+ private boolean mIsNonBlockable;
+ private Set<NotificationChannel> mUniqueChannelsInRow;
+ private Drawable mPkgIcon;
+
+ private @Action int mSelectedAction = -1;
+ private boolean mPressedApply;
+ private boolean mPresentingChannelEditorDialog = false;
+
+ private NotificationInfo.OnSettingsClickListener mOnSettingsClickListener;
+ private NotificationGuts mGutsContainer;
+ private ChannelEditorDialogController mChannelEditorDialogController;
+
+ @VisibleForTesting
+ boolean mSkipPost = false;
+
+ @Retention(SOURCE)
+ @IntDef({ACTION_SETTINGS})
+ private @interface Action {}
+ static final int ACTION_SETTINGS = 5;
+
+ private OnClickListener mOnDone = v -> {
+ mPressedApply = true;
+ closeControls(v, true);
+ };
+
+ public PartialConversationInfo(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void bindNotification(
+ PackageManager pm,
+ INotificationManager iNotificationManager,
+ String pkg,
+ NotificationChannel notificationChannel,
+ Set<NotificationChannel> uniqueChannelsInRow,
+ NotificationEntry entry,
+ NotificationInfo.OnSettingsClickListener onSettingsClick,
+ boolean isDeviceProvisioned,
+ boolean isNonBlockable) {
+ mSelectedAction = -1;
+ mINotificationManager = iNotificationManager;
+ mPackageName = pkg;
+ mSbn = entry.getSbn();
+ mPm = pm;
+ mAppName = mPackageName;
+ mOnSettingsClickListener = onSettingsClick;
+ mNotificationChannel = notificationChannel;
+ mAppUid = mSbn.getUid();
+ mDelegatePkg = mSbn.getOpPkg();
+ mIsDeviceProvisioned = isDeviceProvisioned;
+ mIsNonBlockable = isNonBlockable;
+ mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
+ mUniqueChannelsInRow = uniqueChannelsInRow;
+
+ bindHeader();
+ bindActions();
+
+ View turnOffButton = findViewById(R.id.turn_off_notifications);
+ turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
+ turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonBlockable
+ ? VISIBLE : GONE);
+
+ View done = findViewById(R.id.done);
+ done.setOnClickListener(mOnDone);
+ }
+
+ private void bindActions() {
+ final View settingsButton = findViewById(R.id.info);
+ settingsButton.setOnClickListener(getSettingsOnClickListener());
+ settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
+
+ TextView msg = findViewById(R.id.non_configurable_text);
+ msg.setText(getResources().getString(R.string.no_shortcut, mAppName));
+ }
+
+ private void bindHeader() {
+ bindConversationDetails();
+
+ // Delegate
+ bindDelegate();
+ }
+
+ private OnClickListener getSettingsOnClickListener() {
+ if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
+ final int appUidF = mAppUid;
+ return ((View view) -> {
+ mOnSettingsClickListener.onClick(view, mNotificationChannel, appUidF);
+ });
+ }
+ return null;
+ }
+
+ private OnClickListener getTurnOffNotificationsClickListener() {
+ return ((View view) -> {
+ if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) {
+ mPresentingChannelEditorDialog = true;
+
+ mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
+ mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mChannelEditorDialogController.setOnFinishListener(() -> {
+ mPresentingChannelEditorDialog = false;
+ closeControls(this, false);
+ });
+ mChannelEditorDialogController.show();
+ }
+ });
+ }
+
+ private void bindConversationDetails() {
+ final TextView channelName = findViewById(R.id.parent_channel_name);
+ channelName.setText(mNotificationChannel.getName());
+
+ bindGroup();
+ bindName();
+ bindPackage();
+ bindIcon();
+ }
+
+ private void bindName() {
+ TextView name = findViewById(R.id.name);
+ Bundle extras = mSbn.getNotification().extras;
+ String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
+ if (TextUtils.isEmpty(nameString)) {
+ nameString = extras.getString(Notification.EXTRA_TITLE);
+ }
+ name.setText(nameString);
+ }
+
+ private void bindIcon() {
+ ImageView image = findViewById(R.id.conversation_icon);
+ if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
+ // TODO: maybe use a generic group icon, or a composite of recent senders
+ image.setImageDrawable(mPkgIcon);
+ } else {
+ final List<Notification.MessagingStyle.Message> messages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+ (Parcelable[]) mSbn.getNotification().extras.get(
+ Notification.EXTRA_MESSAGES));
+
+ final Notification.MessagingStyle.Message latestMessage =
+ Notification.MessagingStyle.findLatestIncomingMessage(messages);
+ Icon personIcon = null;
+ if (latestMessage != null && latestMessage.getSenderPerson() != null) {
+ personIcon = latestMessage.getSenderPerson().getIcon();
+ }
+ if (personIcon != null) {
+ image.setImageIcon(latestMessage.getSenderPerson().getIcon());
+ } else {
+ image.setImageDrawable(mPkgIcon);
+ }
+ }
+ }
+
+ private void bindPackage() {
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ mPackageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ mAppName = String.valueOf(mPm.getApplicationLabel(info));
+ mPkgIcon = mPm.getApplicationIcon(info);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ mPkgIcon = mPm.getDefaultActivityIcon();
+ }
+ ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
+ }
+
+ private void bindDelegate() {
+ TextView delegateView = findViewById(R.id.delegate_name);
+
+ if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+ // this notification was posted by a delegate!
+ delegateView.setVisibility(View.VISIBLE);
+ } else {
+ delegateView.setVisibility(View.GONE);
+ }
+ }
+
+ private void bindGroup() {
+ // Set group information if this channel has an associated group.
+ CharSequence groupName = null;
+ if (mNotificationChannel != null && mNotificationChannel.getGroup() != null) {
+ try {
+ final NotificationChannelGroup notificationChannelGroup =
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mNotificationChannel.getGroup(), mPackageName, mAppUid);
+ if (notificationChannelGroup != null) {
+ groupName = notificationChannelGroup.getName();
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ TextView groupNameView = findViewById(R.id.group_name);
+ View groupDivider = findViewById(R.id.group_divider);
+ if (groupName != null) {
+ groupNameView.setText(groupName);
+ groupNameView.setVisibility(VISIBLE);
+ groupDivider.setVisibility(VISIBLE);
+ } else {
+ groupNameView.setVisibility(GONE);
+ groupDivider.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ public boolean post(Runnable action) {
+ if (mSkipPost) {
+ action.run();
+ return true;
+ } else {
+ return super.post(action);
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ }
+
+ @Override
+ public void onFinishedClosing() {
+ // TODO: do we need to do anything here?
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ if (mGutsContainer != null &&
+ event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ if (mGutsContainer.isExposed()) {
+ event.getText().add(mContext.getString(
+ R.string.notification_channel_controls_opened_accessibility, mAppName));
+ } else {
+ event.getText().add(mContext.getString(
+ R.string.notification_channel_controls_closed_accessibility, mAppName));
+ }
+ }
+ }
+
+ /**
+ * Closes the controls and commits the updated importance values (indirectly).
+ *
+ * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
+ * user does not have the ability to undo the action anymore.
+ */
+ @VisibleForTesting
+ void closeControls(View v, boolean save) {
+ int[] parentLoc = new int[2];
+ int[] targetLoc = new int[2];
+ mGutsContainer.getLocationOnScreen(parentLoc);
+ v.getLocationOnScreen(targetLoc);
+ final int centerX = v.getWidth() / 2;
+ final int centerY = v.getHeight() / 2;
+ final int x = targetLoc[0] - parentLoc[0] + centerX;
+ final int y = targetLoc[1] - parentLoc[1] + centerY;
+ mGutsContainer.closeControls(x, y, save, false /* force */);
+ }
+
+ @Override
+ public void setGutsParent(NotificationGuts guts) {
+ mGutsContainer = guts;
+ }
+
+ @Override
+ public boolean willBeRemoved() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldBeSaved() {
+ return mPressedApply;
+ }
+
+ @Override
+ public View getContentView() {
+ return this;
+ }
+
+ @Override
+ public boolean handleCloseControls(boolean save, boolean force) {
+ return false;
+ }
+
+ @Override
+ public int getActualHeight() {
+ return getHeight();
+ }
+
+ @VisibleForTesting
+ public boolean isAnimating() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 6193a8e..428de9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Log;
import android.view.View;
import android.view.ViewParent;
@@ -47,6 +48,8 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.concurrent.atomic.AtomicReference;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -54,7 +57,8 @@
*/
@Singleton
public class StatusBarRemoteInputCallback implements Callback, Callbacks,
- StatusBarStateController.StateListener {
+ StatusBarStateController.StateListener, KeyguardStateController.Callback {
+ private static final String TAG = StatusBarRemoteInputCallback.class.getSimpleName();
private final KeyguardStateController mKeyguardStateController;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -72,6 +76,7 @@
private int mDisabled2;
protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
private Handler mMainHandler = new Handler();
+ private final AtomicReference<Intent> mPendingConfirmCredentialIntent = new AtomicReference();
/**
*/
@@ -98,6 +103,9 @@
mCommandQueue.addCallback(this);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mGroupManager = groupManager;
+ // Listen to onKeyguardShowingChanged in case a managed profile needs to be unlocked
+ // once the primary profile's keyguard is no longer shown.
+ mKeyguardStateController.addCallback(this);
}
@Override
@@ -201,12 +209,39 @@
// Clear pending remote view, as we do not want to trigger pending remote input view when
// it's called by other code
mPendingWorkRemoteInputView = null;
- // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
- final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
- null, userId);
+
+ final Intent newIntent = createConfirmDeviceCredentialIntent(
+ userId, intendSender, notificationKey);
if (newIntent == null) {
+ Log.w(TAG, String.format("Cannot create intent to unlock user %d", userId));
return false;
}
+
+ mPendingConfirmCredentialIntent.set(newIntent);
+
+ // If the Keyguard is currently showing, starting the ConfirmDeviceCredentialActivity
+ // would cause it to pause, not letting the user actually unlock the managed profile.
+ // Instead, wait until we receive a callback indicating it is no longer showing and
+ // then start the pending intent.
+ if (mKeyguardStateController.isShowing()) {
+ // Do nothing, since the callback will get the pending intent and start it.
+ Log.w(TAG, String.format("Keyguard is showing, waiting until it's not"));
+ } else {
+ startPendingConfirmDeviceCredentialIntent();
+ }
+
+ return true;
+ }
+
+ private Intent createConfirmDeviceCredentialIntent(
+ int userId, IntentSender intendSender, String notificationKey) {
+ final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
+ null, userId);
+
+ if (newIntent == null) {
+ return null;
+ }
+
final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
@@ -222,14 +257,40 @@
newIntent.putExtra(
Intent.EXTRA_INTENT,
callBackPendingIntent.getIntentSender());
+
+ return newIntent;
+ }
+
+ private void startPendingConfirmDeviceCredentialIntent() {
+ final Intent pendingIntent = mPendingConfirmCredentialIntent.getAndSet(null);
+ if (pendingIntent == null) {
+ return;
+ }
+
try {
- ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
+ if (mKeyguardStateController.isShowing()) {
+ Log.w(TAG, "Keyguard is showing while starting confirm device credential intent.");
+ }
+ ActivityManager.getService().startConfirmDeviceCredentialIntent(pendingIntent,
null /*options*/);
} catch (RemoteException ex) {
// ignore
}
- return true;
- // End old BaseStatusBar.startWorkChallengeIfNecessary.
+ }
+
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (mKeyguardStateController.isShowing()) {
+ // In order to avoid jarring UX where/ the managed profile challenge is shown and
+ // immediately dismissed, do not attempt to start the confirm device credential
+ // activity if the keyguard is still showing.
+ if (mPendingConfirmCredentialIntent.get() != null) {
+ Log.w(TAG, "There's a pending unlock intent but keyguard is still showing, abort.");
+ }
+ return;
+ }
+
+ startPendingConfirmDeviceCredentialIntent();
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 3847028..dbf40e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -386,7 +386,10 @@
applicationInfo);
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
- NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(mSbn)
+ .setShortcutInfo(mShortcutInfo)
+ .build();
mNotificationInfo.bindNotification(
mShortcutManager,
mMockPackageManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
new file mode 100644
index 0000000..c390e39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.Person;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PartialConversationInfoTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test_package";
+ private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
+ private static final int TEST_UID = 1;
+ private static final String TEST_CHANNEL = "test_channel";
+ private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
+
+ private TestableLooper mTestableLooper;
+ private PartialConversationInfo mInfo;
+ private NotificationChannel mNotificationChannel;
+ private NotificationChannel mDefaultNotificationChannel;
+ private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
+ private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
+ private StatusBarNotification mSbn;
+ private NotificationEntry mEntry;
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private INotificationManager mMockINotificationManager;
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Mock
+ private Icon mIcon;
+ @Mock
+ private Drawable mDrawable;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestableLooper = TestableLooper.get(this);
+
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+ mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+ // Inflate the layout
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ mInfo = (PartialConversationInfo) layoutInflater.inflate(R.layout.partial_conversation_info,
+ null);
+ mInfo.setGutsParent(mock(NotificationGuts.class));
+ // Our view is never attached to a window so the View#post methods in NotificationInfo never
+ // get called. Setting this will skip the post and do the action immediately.
+ mInfo.mSkipPost = true;
+
+ // PackageManager must return a packageInfo and applicationInfo.
+ final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+ .thenReturn(packageInfo);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = TEST_UID; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
+ applicationInfo);
+ final PackageInfo systemPackageInfo = new PackageInfo();
+ systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
+ when(mMockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt()))
+ .thenReturn(systemPackageInfo);
+ when(mMockPackageManager.getPackageInfo(eq("android"), anyInt()))
+ .thenReturn(packageInfo);
+
+ // Package has one channel by default.
+ when(mMockINotificationManager.getNumNotificationChannelsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(1);
+
+ when(mIcon.loadDrawable(any())).thenReturn(mDrawable);
+
+ // Some test channels.
+ mNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
+ mNotificationChannelSet.add(mNotificationChannel);
+ mDefaultNotificationChannel = new NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
+ IMPORTANCE_LOW);
+ mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ }
+
+ @Test
+ public void testBindNotification_SetsTextApplicationName() throws Exception {
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final TextView textView = mInfo.findViewById(R.id.pkg_name);
+ assertTrue(textView.getText().toString().contains("App Name"));
+ assertEquals(VISIBLE, mInfo.findViewById(R.id.header).getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_groupSetsPackageIcon() {
+ mEntry.getSbn().getNotification().extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, true);
+ final Drawable iconDrawable = mock(Drawable.class);
+ when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
+ .thenReturn(iconDrawable);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final ImageView iconView = mInfo.findViewById(R.id.conversation_icon);
+ assertEquals(iconDrawable, iconView.getDrawable());
+ }
+
+ @Test
+ public void testBindNotification_notGroupSetsMessageIcon() {
+ Notification n = new Notification.Builder(mContext, TEST_CHANNEL_NAME)
+ .setStyle(new Notification.MessagingStyle(
+ new Person.Builder().setName("me").build())
+ .addMessage(new Notification.MessagingStyle.Message("hello", 0,
+ new Person.Builder().setName("friend").setIcon(mIcon).build())))
+ .build();
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+ n, UserHandle.CURRENT, null, 0);
+ mEntry.setSbn(mSbn);
+ mEntry.getSbn().getNotification().extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, false);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final ImageView iconView = mInfo.findViewById(R.id.conversation_icon);
+ assertEquals(mDrawable.hashCode() + "", mDrawable, iconView.getDrawable());
+ }
+
+ @Test
+ public void testBindNotification_noDelegate() {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final TextView nameView = mInfo.findViewById(R.id.delegate_name);
+ assertEquals(GONE, nameView.getVisibility());
+ final TextView dividerView = mInfo.findViewById(R.id.group_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_delegate() throws Exception {
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = 7; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+ applicationInfo);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+ NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ entry,
+ null,
+ true,
+ false);
+ final TextView nameView = mInfo.findViewById(R.id.delegate_name);
+ assertEquals(VISIBLE, nameView.getVisibility());
+ assertTrue(nameView.getText().toString().contains("Proxied"));
+ }
+
+ @Test
+ public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final TextView groupNameView = mInfo.findViewById(R.id.group_name);
+ assertEquals(GONE, groupNameView.getVisibility());
+ final TextView dividerView = mInfo.findViewById(R.id.group_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_SetsGroupNameIfNonNull() throws Exception {
+ mNotificationChannel.setGroup("test_group_id");
+ final NotificationChannelGroup notificationChannelGroup =
+ new NotificationChannelGroup("test_group_id", "Test Group Name");
+ when(mMockINotificationManager.getNotificationChannelGroupForPackage(
+ eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(notificationChannelGroup);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final TextView groupNameView = mInfo.findViewById(R.id.group_name);
+ assertEquals(View.VISIBLE, groupNameView.getVisibility());
+ assertEquals("Test Group Name", groupNameView.getText());
+ final TextView dividerView = mInfo.findViewById(R.id.group_divider);
+ assertEquals(View.VISIBLE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_SetsTextChannelName() {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final TextView textView = mInfo.findViewById(R.id.parent_channel_name);
+ assertEquals(TEST_CHANNEL_NAME, textView.getText());
+ }
+
+ @Test
+ public void testBindNotification_SetsOnClickListenerForSettings() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ (View v, NotificationChannel c, int appUid) -> {
+ assertEquals(mNotificationChannel, c);
+ latch.countDown();
+ },
+ true,
+ false);
+
+ final View settingsButton = mInfo.findViewById(R.id.info);
+ settingsButton.performClick();
+ // Verify that listener was triggered.
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ false);
+ final View settingsButton = mInfo.findViewById(R.id.info);
+ assertTrue(settingsButton.getVisibility() != View.VISIBLE);
+ }
+
+ @Test
+ public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ (View v, NotificationChannel c, int appUid) -> {
+ assertEquals(mNotificationChannel, c);
+ },
+ false,
+ false);
+ final View settingsButton = mInfo.findViewById(R.id.info);
+ assertTrue(settingsButton.getVisibility() != View.VISIBLE);
+ }
+
+ @Test
+ public void testBindNotification_whenAppUnblockable() {
+ mInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ true,
+ true);
+
+ assertEquals(GONE,
+ mInfo.findViewById(R.id.turn_off_notifications).getVisibility());
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 83ca699..2f60e42 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -47,7 +47,7 @@
NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel(
record.getSbn().getPackageName(),
record.getSbn().getUid(), record.getChannel().getId(),
- record.getSbn().getShortcutId(mContext), true, false);
+ record.getSbn().getShortcutId(), true, false);
record.updateNotificationChannel(updatedChannel);
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6c3177f..2f76a1f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2718,12 +2718,12 @@
}
return text == null ? null : String.valueOf(text);
}
-
+
protected void maybeRegisterMessageSent(NotificationRecord r) {
Context appContext = r.getSbn().getPackageContext(getContext());
- Notification.Builder nb =
+ Notification.Builder nb =
Notification.Builder.recoverBuilder(appContext, r.getNotification());
- if (nb.getStyle() instanceof Notification.MessagingStyle) {
+ if (nb.getStyle() instanceof Notification.MessagingStyle && r.getShortcutInfo() == null) {
mPreferencesHelper.setMessageSent(r.getSbn().getPackageName(), r.getUid());
handleSavePolicyFile();
}
@@ -5627,7 +5627,7 @@
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
- String shortcutId = n.getShortcutId(getContext());
+ String shortcutId = n.getShortcutId();
final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
pkg, notificationUid, channelId, shortcutId,
true /* parent ok */, false /* includeDeleted */);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e45b41d..8e3de15 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1386,10 +1386,6 @@
|| !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
return false;
}
- if (mShortcutInfo == null && Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 1) {
- return false;
- }
if (mIsNotConversationOverride) {
return false;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 6d7b410..a4b99b3 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -116,7 +116,7 @@
private static final String ATT_ENABLED = "enabled";
private static final String ATT_USER_ALLOWED = "allowed";
private static final String ATT_HIDE_SILENT = "hide_gentle";
- private static final String ATT_SENT_MESSAGE = "sent_msg";
+ private static final String ATT_SENT_MESSAGE = "sent_invalid_msg";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -194,8 +194,6 @@
updateBadgingEnabled();
updateBubblesEnabled();
syncChannelsBypassingDnd(mContext.getUserId());
- mAllowInvalidShortcuts = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0;
}
public void readXml(XmlPullParser parser, boolean forRestore, int userId)
@@ -1313,7 +1311,9 @@
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
+ if (!TextUtils.isEmpty(nc.getConversationId())
+ && !nc.isDeleted()
+ && !nc.isDemoted()) {
ConversationChannelWrapper conversation = new ConversationChannelWrapper();
conversation.setPkg(r.pkg);
conversation.setUid(r.uid);
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 13892ba0..94f69e9 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -152,9 +152,13 @@
if (shortcutInfo == null || !shortcutInfo.isLongLived() || !shortcutInfo.isEnabled()) {
return false;
}
- return mShortcutServiceInternal.isSharingShortcut(callingUserId, "android",
+ // TODO (b/155016294) uncomment when sharing shortcuts are required
+ /*
+ mShortcutServiceInternal.isSharingShortcut(callingUserId, "android",
shortcutInfo.getPackage(), shortcutInfo.getId(), shortcutInfo.getUserId(),
SHARING_FILTER);
+ */
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 982df4b..5a27f47 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1431,15 +1431,15 @@
@Override
void removeChild(WindowContainer child) {
- // A rootable child task that is now being removed from an organized task. Making sure
- // the stack references is keep updated.
- if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
- getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) child);
- }
removeChild(child, "removeChild");
}
void removeChild(WindowContainer r, String reason) {
+ // A rootable child task that is now being removed from an organized task. Making sure
+ // the stack references is keep updated.
+ if (mTaskOrganizer != null && mCreatedByOrganizer && r.asTask() != null) {
+ getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) r);
+ }
if (!mChildren.contains(r)) {
Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this);
return;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index a23ade6..77ce2f0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -76,8 +76,6 @@
@Test
public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
NotificationChannelExtractor extractor = new NotificationChannelExtractor();
extractor.setConfig(mConfig);
@@ -96,7 +94,7 @@
NotificationChannel updatedChannel =
new NotificationChannel("a", "", IMPORTANCE_HIGH);
when(mConfig.getConversationNotificationChannel(
- any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)),
+ any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId()),
eq(true), eq(false)))
.thenReturn(updatedChannel);
@@ -106,8 +104,6 @@
@Test
public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
NotificationChannelExtractor extractor = new NotificationChannelExtractor();
extractor.setConfig(mConfig);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 41748b8..d5ecfeb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6589,14 +6589,45 @@
}
@Test
- public void testRecordMessages() throws RemoteException {
+ public void testRecordMessages_invalidMsg() throws RemoteException {
NotificationRecord nr =
generateMessageBubbleNotifRecord(mTestNotificationChannel,
- "testRecordMessages");
+ "testRecordMessages_invalidMsg");
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
assertTrue(mBinderService.hasSentMessage(PKG, mUid));
}
+
+ @Test
+ public void testRecordMessages_validMsg() throws RemoteException {
+ // Messaging notification with shortcut info
+ Notification.BubbleMetadata metadata =
+ new Notification.BubbleMetadata.Builder("id").build();
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId("id");
+ nb.setBubbleMetadata(metadata);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "testRecordMessages_validMsg", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ // Pretend the shortcut exists
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.getPackage()).thenReturn(PKG);
+ when(info.getId()).thenReturn("id");
+ when(info.getUserId()).thenReturn(USER_SYSTEM);
+ when(info.isLongLived()).thenReturn(true);
+ when(info.isEnabled()).thenReturn(true);
+ shortcutInfos.add(info);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ assertFalse(mBinderService.hasSentMessage(PKG, mUid));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 3139bfa..9f593ce 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -123,8 +123,6 @@
when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.targetSdkVersion = Build.VERSION_CODES.O;
when(mMockContext.getApplicationInfo()).thenReturn(appInfo);
@@ -1127,18 +1125,7 @@
}
@Test
- public void testIsConversation_nullShortcut() {
- StatusBarNotification sbn = getMessagingStyleNotification();
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.setShortcutInfo(null);
-
- assertFalse(record.isConversation());
- }
-
- @Test
- public void testIsConversation_bypassShortcutFlagEnabled() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
+ public void testIsConversation_noShortcut() {
StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setShortcutInfo(null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e11392b..4320f1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3023,31 +3023,7 @@
}
@Test
- public void testPlaceholderConversationId_shortcutNotRequired() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
-
- mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
- mAppOpsManager);
-
- final String xml = "<ranking version=\"1\">\n"
- + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
- + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
- + "</package>"
- + "</ranking>";
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
- null);
- parser.nextTag();
- mHelper.readXml(parser, false, UserHandle.USER_ALL);
-
- assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
- }
-
- @Test
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager);
@@ -3067,8 +3043,6 @@
@Test
public void testNormalConversationId_shortcutRequired() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager);
@@ -3088,8 +3062,6 @@
@Test
public void testNoConversationId_shortcutRequired() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager);
@@ -3276,6 +3248,19 @@
}
@Test
+ public void testGetConversations_noDemoted() {
+ NotificationChannel parent = new NotificationChannel("parent", "p", 1);
+ mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+ NotificationChannel channel =
+ new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "convo");
+ channel.setDemoted(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+ }
+
+ @Test
public void testGetConversations() {
NotificationChannelGroup group = new NotificationChannelGroup("acct", "account_name");
mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index 3095c87..eb2d9fe 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -40,6 +40,7 @@
import com.android.server.UiServiceTestCase;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -184,6 +185,7 @@
assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull();
}
+ @Ignore("b/155016294")
@Test
public void testGetValidShortcutInfo_notSharingShortcut() {
ShortcutInfo si = mock(ShortcutInfo.class);
@@ -229,8 +231,9 @@
ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
shortcuts.add(si);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
- when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any())).thenReturn(true);
+ // TODO: b/155016294
+ //when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ // anyString(), anyInt(), any())).thenReturn(true);
assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 6c209e4..5227f3c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -234,6 +234,25 @@
}
@Test
+ public void testRemoveOrganizedTask_UpdateStackReference() {
+ final ActivityStack rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
+ final ActivityRecord homeActivity = new ActivityBuilder(mService)
+ .setStack(rootHomeTask)
+ .setCreateTask(true)
+ .build();
+ final ActivityStack secondaryStack = (ActivityStack) WindowContainer.fromBinder(
+ mService.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(),
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token.asBinder());
+
+ rootHomeTask.reparent(secondaryStack, POSITION_TOP);
+ assertEquals(secondaryStack, rootHomeTask.getParent());
+
+ // This should call to {@link TaskDisplayArea#removeStackReferenceIfNeeded}.
+ homeActivity.removeImmediately();
+ assertNull(mDefaultTaskDisplayArea.getRootHomeTask());
+ }
+
+ @Test
public void testStackInheritsDisplayWindowingMode() {
final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);