Merge "Show a line below the tabs in tabbed view to align with UX mocks."
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 526c0b3..948546b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+ ParceledListSlice getConversations(boolean onlyImportant);
ParceledListSlice getConversationsForPackage(String pkg, int uid);
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f86c971..0fa261a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8595,6 +8595,15 @@
public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
/**
+ * When enabled conversations marked as favorites will be set to bubble.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String BUBBLE_IMPORTANT_CONVERSATIONS
+ = "bubble_important_conversations";
+
+ /**
* Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
* swipe).
*
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 9847695..ab465ab 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -33,6 +33,8 @@
private CharSequence mGroupLabel;
private CharSequence mParentChannelLabel;
private ShortcutInfo mShortcutInfo;
+ private String mPkg;
+ private int mUid;
public ConversationChannelWrapper() {}
@@ -41,6 +43,8 @@
mGroupLabel = in.readCharSequence();
mParentChannelLabel = in.readCharSequence();
mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mPkg = in.readStringNoHelper();
+ mUid = in.readInt();
}
@Override
@@ -49,6 +53,8 @@
dest.writeCharSequence(mGroupLabel);
dest.writeCharSequence(mParentChannelLabel);
dest.writeParcelable(mShortcutInfo, flags);
+ dest.writeStringNoHelper(mPkg);
+ dest.writeInt(mUid);
}
@Override
@@ -103,6 +109,22 @@
mShortcutInfo = shortcutInfo;
}
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public void setPkg(String pkg) {
+ mPkg = pkg;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public void setUid(int uid) {
+ mUid = uid;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -111,12 +133,14 @@
return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
- Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+ Objects.equals(getShortcutInfo(), that.getShortcutInfo()) &&
+ Objects.equals(getPkg(), that.getPkg()) &&
+ getUid() == that.getUid();
}
@Override
public int hashCode() {
return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
- getShortcutInfo());
+ getShortcutInfo(), getPkg(), getUid());
}
}
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index d21a952..850e9fc 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -406,9 +406,7 @@
}
currentIndex += RIGHT_MARKER.length();
} else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
- if (!mPositionFromCenterVertical) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += BOTTOM_MARKER.length();
/* prepare to parse the rest path */
@@ -416,9 +414,7 @@
mBindBottomCutout = true;
mPositionFromBottom = true;
} else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
- if (!mPositionFromBottom) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += CENTER_VERTICAL_MARKER.length();
/* prepare to parse the rest path */
@@ -431,14 +427,16 @@
/* prepare to parse the rest path */
resetStatus(sb);
} else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindRightCutout) {
- mBindLeftCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindRightCutout = false;
+ mBindLeftCutout = true;
+
currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
} else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindLeftCutout) {
- mBindRightCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindLeftCutout = false;
+ mBindRightCutout = true;
+
currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
} else {
currentIndex += 1;
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index e8f84aa..b4a0208 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.AppGlobals;
+import android.app.admin.DevicePolicyEventLogger;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +28,7 @@
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -251,6 +253,8 @@
abstract @Nullable ViewGroup getInactiveAdapterView();
+ abstract String getMetricsCategory();
+
/**
* Rebuilds the tab that is currently visible to the user.
* <p>Returns {@code true} if rebuild has completed.
@@ -282,6 +286,10 @@
UserHandle listUserHandle = activeListAdapter.getUserHandle();
if (listUserHandle == mWorkProfileUserHandle
&& mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
R.string.resolver_turn_on_work_apps,
@@ -294,11 +302,19 @@
if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
if (listUserHandle == mPersonalProfileUserHandle) {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
R.string.resolver_cant_share_with_personal_apps,
R.string.resolver_cant_share_cross_profile_explanation);
} else {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
R.string.resolver_cant_share_with_work_apps,
@@ -315,6 +331,12 @@
UserHandle listUserHandle = listAdapter.getUserHandle();
if (UserHandle.myUserId() == listUserHandle.getIdentifier()
|| !hasAppsInOtherProfile(listAdapter)) {
+ if (mWorkProfileUserHandle != null) {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
+ .setStrings(getMetricsCategory())
+ .write();
+ }
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
R.string.resolver_no_apps_available,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 16db248..a201a33 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2698,6 +2698,11 @@
@Override
protected void resetButtonBar() {}
+ @Override
+ protected String getMetricsCategory() {
+ return METRICS_CATEGORY_CHOOSER;
+ }
+
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index e350142..d440402 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -165,6 +165,11 @@
return getListViewForIndex(1 - getCurrentPage());
}
+ @Override
+ String getMetricsCategory() {
+ return ResolverActivity.METRICS_CATEGORY_CHOOSER;
+ }
+
class ChooserProfileDescriptor extends ProfileDescriptor {
private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 36d848c..96bfe73 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -33,6 +33,7 @@
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
+import android.app.admin.DevicePolicyEventLogger;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -60,6 +61,7 @@
import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -84,6 +86,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
+import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.content.PackageMonitor;
@@ -99,6 +102,7 @@
import java.util.Objects;
import java.util.Set;
+
/**
* This activity is displayed when the system attempts to start an Intent for
* which there is more than one matching activity, allowing the user to decide
@@ -152,6 +156,8 @@
private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
+ protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
+ protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
/**
* TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
@@ -1201,12 +1207,14 @@
if (!mSafeForwardingMode) {
if (cti.startAsUser(this, null, currentUserHandle)) {
onActivityStarted(cti);
+ maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
}
return;
}
try {
if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
onActivityStarted(cti);
+ maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
}
} catch (RuntimeException e) {
String launchedFromPackage;
@@ -1222,6 +1230,18 @@
}
}
+ private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
+ if (!hasWorkProfile() || currentUserHandle == getUser()) {
+ return;
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
+ .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+ .setStrings(getMetricsCategory(),
+ cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
+ .write();
+ }
+
public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
int userId) {
@@ -1425,7 +1445,8 @@
* - The target app has declared it supports cross-profile communication via manifest metadata
*/
private boolean maybeAutolaunchIfCrossProfileSupported() {
- int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
+ ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+ int count = activeListAdapter.getUnfilteredCount();
if (count != 1) {
return false;
}
@@ -1434,7 +1455,7 @@
if (inactiveListAdapter.getUnfilteredCount() != 1) {
return false;
}
- TargetInfo activeProfileTarget = mMultiProfilePagerAdapter.getActiveListAdapter()
+ TargetInfo activeProfileTarget = activeListAdapter
.targetInfoForPosition(0, false);
TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
@@ -1449,6 +1470,11 @@
return false;
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
+ .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+ .setStrings(getMetricsCategory())
+ .write();
safelyStartActivity(activeProfileTarget);
finish();
return true;
@@ -1530,6 +1556,11 @@
viewPager.setCurrentItem(1);
}
setupViewVisibilities();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
+ .setInt(viewPager.getCurrentItem())
+ .setStrings(getMetricsCategory())
+ .write();
});
viewPager.setVisibility(View.VISIBLE);
@@ -1698,6 +1729,10 @@
&& Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
}
+ protected String getMetricsCategory() {
+ return METRICS_CATEGORY_RESOLVER;
+ }
+
@Override // ResolverListCommunicator
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 96dc83a..21e7fd9 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -150,6 +150,11 @@
return getListViewForIndex(1 - getCurrentPage());
}
+ @Override
+ String getMetricsCategory() {
+ return ResolverActivity.METRICS_CATEGORY_RESOLVER;
+ }
+
class ResolverProfileDescriptor extends ProfileDescriptor {
private ResolverListAdapter resolverListAdapter;
final ListView listView;
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 782b269..3d8108d 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -179,4 +179,11 @@
PROVISIONING_DPC_SETUP_STARTED = 152;
PROVISIONING_DPC_SETUP_COMPLETED = 153;
PROVISIONING_ORGANIZATION_OWNED_MANAGED_PROFILE = 154;
+ RESOLVER_CROSS_PROFILE_TARGET_OPENED = 155;
+ RESOLVER_SWITCH_TABS = 156;
+ RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED = 157;
+ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL= 158;
+ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159;
+ RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160;
+ RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 488698a..6530036 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4953,16 +4953,6 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
- <!-- Feature Id for Country Detector. -->
- <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
- <!-- Feature Id for Location service. -->
- <feature android:featureId="LocationService" android:label="@string/location_service"/>
- <!-- Feature Id for Sensor Notification service. -->
- <feature android:featureId="SensorNotificationService"
- android:label="@string/sensor_notification_service"/>
- <!-- Feature Id for Twilight service. -->
- <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
-
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e7ad8eb..e6a93e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -421,14 +421,6 @@
[CHAR LIMIT=NONE] -->
<string name="location_changed_notification_text">Tap to see your location settings.</string>
- <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
- <string name="country_detector">Country Detector</string>
- <!-- Feature Id for Location service. [CHAR LIMIT=NONE]-->
- <string name="location_service">Location Service</string>
- <!-- Feature Id for Sensor Notification service. [CHAR LIMIT=NONE]-->
- <string name="sensor_notification_service">Sensor Notification Service</string>
- <!-- Feature Id for Twilight service. [CHAR LIMIT=NONE]-->
- <string name="twilight_service">Twilight Service</string>
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 1f831bb..b41f90c 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -69,6 +69,13 @@
+ "z\n"
+ "@right\n"
+ "@bind_right_cutout\n"
+ + "@bottom\n"
+ + "M 0,0\n"
+ + "h -24\n"
+ + "v -48\n"
+ + "h 48\n"
+ + "v 48\n"
+ + "z\n"
+ "@dp";
private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
+ "h 1\n"
@@ -141,13 +148,66 @@
}
@Test
+ public void parse_withBindMarker_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
public void parse_withBindMarker_shouldHaveRightBound() {
CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
}
@Test
- public void parse_tallCutout_shouldBeDone() {
+ public void parse_withBindMarker_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(456, 1752, 624, 1920));
+ }
+
+ @Test
+ public void parse_withBindMarker_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 168, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveLeftBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 540, 168, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveRightBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(1752, 540, 1920, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(876, 912, 1044, 1080));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_tallCutout_topBoundShouldMatchExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "L -44.3940446283, 36.0595537175\n"
@@ -162,7 +222,7 @@
}
@Test
- public void parse_wideCutout_shouldBeDone() {
+ public void parse_wideCutout_topBoundShouldMatchExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -177,7 +237,7 @@
}
@Test
- public void parse_narrowCutout_shouldBeDone() {
+ public void parse_narrowCutout_topBoundShouldHaveExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -24, 0\n"
+ "L -21.9940446283, 20.0595537175\n"
@@ -192,7 +252,7 @@
}
@Test
- public void parse_doubleCutout_shouldBeDone() {
+ public void parse_doubleCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -217,7 +277,7 @@
}
@Test
- public void parse_cornerCutout_shouldBeDone() {
+ public void parse_cornerCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "C -48,48 -48,48 0,48\n"
@@ -229,7 +289,7 @@
}
@Test
- public void parse_holeCutout_shouldBeDone() {
+ public void parse_holeCutout_shouldMatchExpectedInset() {
CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
+ "h 136\n"
+ "v 136\n"
@@ -259,4 +319,38 @@
assertThat(cutoutSpecification.getSafeInset())
.isEqualTo(new Rect(6, 0, 8, 0));
}
+
+ @Test
+ public void parse_bottomLeftSpec_withBindLeftMarker_shouldBeLeftBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@left\n"
+ + "@bind_left_cutout");
+
+ assertThat(cutoutSpecification.getLeftBound())
+ .isEqualTo(new Rect(0, 190, 10, 200));
+ }
+
+ @Test
+ public void parse_bottomRightSpec_withBindRightMarker_shouldBeRightBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@right\n"
+ + "@bind_right_cutout");
+
+ assertThat(cutoutSpecification.getRightBound())
+ .isEqualTo(new Rect(390, 190, 400, 200));
+ }
}
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 248e5fe..60eda06 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
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -112,7 +113,8 @@
boolean mSkipPost = false;
@Retention(SOURCE)
- @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+ @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
+ ACTION_UNBUBBLE, ACTION_SETTINGS})
private @interface Action {}
static final int ACTION_BUBBLE = 0;
static final int ACTION_HOME = 1;
@@ -128,8 +130,6 @@
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
- Settings.Global.putInt(
- mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
}
closeControls(v, true);
};
@@ -225,7 +225,9 @@
mShortcutInfo = shortcuts.get(0);
}
- mIsBubbleable = mEntry.getBubbleMetadata() != null;
+ mIsBubbleable = mEntry.getBubbleMetadata() != null
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
mStartedAsBubble = mEntry.isBubble();
createConversationChannelIfNeeded();
@@ -382,6 +384,11 @@
((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
}
+ private boolean bubbleImportantConversations() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
+ }
+
private void bindDelegate() {
TextView delegateView = findViewById(R.id.delegate_name);
@@ -579,6 +586,10 @@
case ACTION_FAVORITE:
mChannelToUpdate.setImportantConversation(
!mChannelToUpdate.isImportantConversation());
+ if (mChannelToUpdate.isImportantConversation()
+ && bubbleImportantConversations()) {
+ mChannelToUpdate.setAllowBubbles(true);
+ }
break;
case ACTION_MUTE:
if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
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 27b263f..138ea39 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
@@ -21,6 +21,8 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -55,6 +57,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -449,6 +452,7 @@
@Test
public void testBindNotification_bubbleActionVisibleWhenCanBubble() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -469,7 +473,8 @@
}
@Test
- public void testBindNotification_bubbleActionVisibleWhenCannotBubble() {
+ public void testBindNotification_bubbleAction_noBubbleMetadata() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -490,6 +495,28 @@
}
@Test
+ public void testBindNotification_bubbleActionGloballyOff() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mBubbleEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
+ assertEquals(View.GONE, bubbleView.getVisibility());
+ }
+
+ @Test
public void testAddToHome() throws Exception {
when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true);
@@ -550,6 +577,7 @@
@Test
public void testBubble_promotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationChannel.setAllowBubbles(false);
mConversationChannel.setAllowBubbles(false);
@@ -584,6 +612,7 @@
@Test
public void testBubble_demotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
@@ -617,6 +646,7 @@
@Test
public void testBubble_noChannelChange() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -645,7 +675,11 @@
}
@Test
- public void testFavorite_favorite() throws Exception {
+ public void testFavorite_favorite_noBubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 0);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -673,6 +707,44 @@
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), anyInt(), captor.capture());
assertTrue(captor.getValue().isImportantConversation());
+ assertFalse(captor.getValue().canBubble());
+ verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry);
+ }
+
+ @Test
+ public void testFavorite_favorite_bubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+ assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+ fave.getContentDescription().toString());
+
+ fave.performClick();
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<NotificationChannel> captor =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), anyInt(), captor.capture());
+ assertTrue(captor.getValue().isImportantConversation());
+ assertTrue(captor.getValue().canBubble());
}
@Test
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 207a6aa..6852fbf 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -194,8 +194,6 @@
// time
private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- private static final String FEATURE_ID = "LocationService";
-
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Object mLock = new Object();
@@ -245,7 +243,7 @@
private int mBatterySaverMode;
private LocationManagerService(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ mContext = context;
mHandler = FgThread.getHandler();
mLocalService = new LocalService();
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -16,8 +16,10 @@
package com.android.server;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorAdditionalInfo;
@@ -29,7 +31,9 @@
import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
public class SensorNotificationService extends SystemService
@@ -48,8 +52,6 @@
private static final long MILLIS_2010_1_1 = 1262358000000l;
- private static final String FEATURE_ID = "SensorNotificationService";
-
private Context mContext;
private SensorManager mSensorManager;
private LocationManager mLocationManager;
@@ -59,8 +61,8 @@
private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
public SensorNotificationService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
- mContext = getContext();
+ super(context);
+ mContext = context;
}
public void onStart() {
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..8326ef9 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,15 +31,13 @@
* @hide
*/
public abstract class CountryDetectorBase {
- private static final String FEATURE_ID = "CountryDetector";
-
protected final Handler mHandler;
protected final Context mContext;
protected CountryListener mListener;
protected Country mDetectedCountry;
- public CountryDetectorBase(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ public CountryDetectorBase(Context ctx) {
+ mContext = ctx;
mHandler = new Handler();
}
@@ -47,7 +45,7 @@
* Start detecting the country that the user is in.
*
* @return the country if it is available immediately, otherwise null should
- * be returned.
+ * be returned.
*/
public abstract Country detectCountry();
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 05867ba..f7e1398 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -214,6 +214,8 @@
* Updates the mSessionInfo. Returns true if the session info is changed.
*/
boolean updateSessionInfosIfNeededLocked() {
+ // Prevent to execute this method before mBtRouteProvider is created.
+ if (mBtRouteProvider == null) return false;
RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd86f1d..0d402e5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3440,6 +3440,27 @@
}
@Override
+ public ParceledListSlice<ConversationChannelWrapper> getConversations(
+ boolean onlyImportant) {
+ enforceSystemOrSystemUI("getConversations");
+ ArrayList<ConversationChannelWrapper> conversations =
+ mPreferencesHelper.getConversations(onlyImportant);
+ for (ConversationChannelWrapper conversation : conversations) {
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+ .setPackage(conversation.getPkg())
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setShortcutIds(Arrays.asList(
+ conversation.getNotificationChannel().getConversationId()));
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+ query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
+ if (shortcuts != null && !shortcuts.isEmpty()) {
+ conversation.setShortcutInfo(shortcuts.get(0));
+ }
+ }
+ return new ParceledListSlice<>(conversations);
+ }
+
+ @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 20c8625..b8186ed 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1186,6 +1186,44 @@
return groups;
}
+ public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+ synchronized (mPackagePreferences) {
+ ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+
+ for (PackagePreferences p : mPackagePreferences.values()) {
+ int N = p.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = p.channels.valueAt(i);
+ if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+ && (nc.isImportantConversation() || !onlyImportant)) {
+ ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(p.pkg);
+ conversation.setUid(p.uid);
+ conversation.setNotificationChannel(nc);
+ conversation.setParentChannelLabel(
+ p.channels.get(nc.getParentChannelId()).getName());
+ boolean blockedByGroup = false;
+ if (nc.getGroup() != null) {
+ NotificationChannelGroup group = p.groups.get(nc.getGroup());
+ if (group != null) {
+ if (group.isBlocked()) {
+ blockedByGroup = true;
+ } else {
+ conversation.setGroupLabel(group.getName());
+ }
+ }
+ }
+ if (!blockedByGroup) {
+ conversations.add(conversation);
+ }
+ }
+ }
+ }
+
+ return conversations;
+ }
+ }
+
public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
Objects.requireNonNull(pkg);
synchronized (mPackagePreferences) {
@@ -1199,6 +1237,8 @@
final NotificationChannel nc = r.channels.valueAt(i);
if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(r.pkg);
+ conversation.setUid(r.uid);
conversation.setNotificationChannel(nc);
conversation.setParentChannelLabel(
r.channels.get(nc.getParentChannelId()).getName());
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..e72ba8d 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,6 @@
implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
private static final String TAG = "TwilightService";
- private static final String FEATURE_ID = "TwilightService";
private static final boolean DEBUG = false;
private static final int MSG_START_LISTENING = 1;
@@ -74,7 +73,7 @@
protected TwilightState mLastTwilightState;
public TwilightService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
+ super(context);
mHandler = new Handler(Looper.getMainLooper(), this);
}
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 0adf15c..7f9732b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@
private static final int UID_N_MR1 = 0;
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
+ private static final int UID_P = 2222;
private static final String SYSTEM_PKG = "android";
private static final int SYSTEM_UID = 1000;
private static final UserHandle USER2 = UserHandle.of(10);
@@ -141,9 +142,11 @@
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_P), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+ when(mPm.getPackageUidAsUser(eq(PKG_P), anyInt())).thenReturn(UID_P);
when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
PackageInfo info = mock(PackageInfo.class);
info.signatures = new Signature[] {mock(Signature.class)};
@@ -2945,6 +2948,92 @@
}
@Test
+ public void testGetConversations_all() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ channel2.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+
+ assertEquals(3, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertTrue(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ @Test
+ public void testGetConversations_onlyImportant() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ channel.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ diffConvo.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+
+ assertEquals(2, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertFalse(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
+ NotificationChannel expected) {
+ for (ConversationChannelWrapper ccw : list) {
+ if (ccw.getNotificationChannel().equals(expected)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Test
public void testGetConversations_invalidPkg() {
assertThat(mHelper.getConversations("bad", 1)).isEmpty();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b5663bd..851b052 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -260,4 +260,9 @@
public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
+ return this;
+ }
}