diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt index fa1b0267fafa5..4de978c77128b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt @@ -18,8 +18,10 @@ package com.android.systemui.settings import android.content.Context import android.os.UserHandle +import androidx.annotation.VisibleForTesting import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.util.Assert +import java.lang.IllegalStateException import javax.inject.Inject import javax.inject.Singleton @@ -32,7 +34,16 @@ class CurrentUserContextTracker @Inject constructor( broadcastDispatcher: BroadcastDispatcher ) { private val userTracker: CurrentUserTracker - var currentUserContext: Context + private var initialized = false + + private var _curUserContext: Context? = null + val currentUserContext: Context + get() { + if (!initialized) { + throw IllegalStateException("Must initialize before getting context") + } + return _curUserContext!! + } init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { @@ -40,21 +51,21 @@ class CurrentUserContextTracker @Inject constructor( handleUserSwitched(newUserId) } } - - currentUserContext = makeUserContext(userTracker.currentUserId) } fun initialize() { + initialized = true + _curUserContext = makeUserContext(userTracker.currentUserId) userTracker.startTracking() } - private fun handleUserSwitched(newUserId: Int) { - currentUserContext = makeUserContext(newUserId) + @VisibleForTesting + fun handleUserSwitched(newUserId: Int) { + _curUserContext = makeUserContext(newUserId) } private fun makeUserContext(uid: Int): Context { Assert.isMainThread() - return sysuiContext.createContextAsUser( - UserHandle.getUserHandleForUid(userTracker.currentUserId), 0) + return sysuiContext.createContextAsUser(UserHandle.of(uid), 0) } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 5236385b3716f..cb0c2838c24df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -378,6 +378,7 @@ public final class NotificationEntry extends ListEntry { /** * Returns the data needed for a bubble for this notification, if it exists. */ + @Nullable public Notification.BubbleMetadata getBubbleMetadata() { return mBubbleMetadata; } @@ -385,7 +386,7 @@ public final class NotificationEntry extends ListEntry { /** * Sets bubble metadata for this notification. */ - public void setBubbleMetadata(Notification.BubbleMetadata metadata) { + public void setBubbleMetadata(@Nullable Notification.BubbleMetadata metadata) { mBubbleMetadata = metadata; } 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 55a5935418192..bcc81a8b967fb 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 @@ -31,6 +31,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; @@ -98,7 +99,7 @@ public class NotificationConversationInfo extends LinearLayout implements private ShortcutInfo mShortcutInfo; private String mConversationId; private StatusBarNotification mSbn; - private Notification.BubbleMetadata mBubbleMetadata; + @Nullable private Notification.BubbleMetadata mBubbleMetadata; private Context mUserContext; private Provider mBuilderProvider; private boolean mIsDeviceProvisioned; @@ -203,6 +204,7 @@ public class NotificationConversationInfo extends LinearLayout implements String pkg, NotificationChannel notificationChannel, NotificationEntry entry, + Notification.BubbleMetadata bubbleMetadata, OnSettingsClickListener onSettingsClick, OnSnoozeClickListener onSnoozeClickListener, ConversationIconFactory conversationIconFactory, @@ -224,7 +226,7 @@ public class NotificationConversationInfo extends LinearLayout implements mOnSnoozeClickListener = onSnoozeClickListener; mIconFactory = conversationIconFactory; mUserContext = userContext; - mBubbleMetadata = entry.getBubbleMetadata(); + mBubbleMetadata = bubbleMetadata; mBuilderProvider = builderProvider; mShortcutManager = shortcutManager; @@ -538,7 +540,8 @@ public class NotificationConversationInfo extends LinearLayout implements Log.e(TAG, "Could not check conversation senders", e); } - boolean showAsBubble = mBubbleMetadata.getAutoExpandBubble() + boolean showAsBubble = mBubbleMetadata != null + && mBubbleMetadata.getAutoExpandBubble() && Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0) == 1; 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 624fabc0a4966..1c808cf90321b 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 @@ -366,7 +366,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx final ExpandableNotificationRow row, NotificationConversationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); - StatusBarNotification sbn = row.getEntry().getSbn(); + NotificationEntry entry = row.getEntry(); + StatusBarNotification sbn = entry.getSbn(); String packageName = sbn.getPackageName(); // Settings link is only valid for notifications that specify a non-system user NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null; @@ -407,8 +408,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mNotificationManager, mVisualStabilityManager, packageName, - row.getEntry().getChannel(), - row.getEntry(), + entry.getChannel(), + entry, + entry.getBubbleMetadata(), onSettingsClick, onSnoozeClickListener, iconFactoryLoader, diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt new file mode 100644 index 0000000000000..628c06a56abd4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt @@ -0,0 +1,92 @@ +/* + * 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.settings + +import android.content.Context +import android.content.ContextWrapper +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class CurrentUserContextTrackerTest : SysuiTestCase() { + + private lateinit var tracker: CurrentUserContextTracker + @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + allowTestableLooperAsMainThread() + + // wrap Context so that tests don't throw for missing package errors + val wrapped = object : ContextWrapper(context) { + override fun createContextAsUser(user: UserHandle, flags: Int): Context { + val mockContext = mock(Context::class.java) + `when`(mockContext.user).thenReturn(user) + `when`(mockContext.userId).thenReturn(user.identifier) + return mockContext + } + } + + tracker = CurrentUserContextTracker(wrapped, broadcastDispatcher) + tracker.initialize() + } + + @Test + fun testContextExistsAfterInit_noCrash() { + tracker.currentUserContext + } + + @Test + fun testUserContextIsCorrectAfterUserSwitch() { + // We always start out with system ui test + assertTrue("Starting userId should be 0", tracker.currentUserContext.userId == 0) + + // WHEN user changes + tracker.handleUserSwitched(1) + + // THEN user context should have the correct userId + assertTrue("User has changed to userId 1, the context should reflect that", + tracker.currentUserContext.userId == 1) + } + + @Suppress("UNUSED_PARAMETER") + @Test(expected = IllegalStateException::class) + fun testContextTrackerThrowsExceptionWhenNotInitialized() { + // GIVEN an uninitialized CurrentUserContextTracker + val userTracker = CurrentUserContextTracker(context, broadcastDispatcher) + + // WHEN client asks for a context + val userContext = userTracker.currentUserContext + + // THEN an exception is thrown + } +} \ No newline at end of file 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 61388b6d0389f..6db868563d3d9 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 @@ -48,9 +48,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; -import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; @@ -91,6 +91,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockito.stubbing.Answer; @@ -147,14 +148,15 @@ public class NotificationConversationInfoTest extends SysuiTestCase { private ShadeController mShadeController; @Mock private ConversationIconFactory mIconFactory; - @Mock - private Context mUserContext; @Mock(answer = Answers.RETURNS_SELF) private PriorityOnboardingDialogController.Builder mBuilder; private Provider mBuilderProvider = () -> mBuilder; + @Mock + private Notification.BubbleMetadata mBubbleMetadata; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); @@ -228,6 +230,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { when(mMockINotificationManager.getConversationNotificationChannel(anyString(), anyInt(), anyString(), eq(TEST_CHANNEL), eq(false), eq(CONVERSATION_ID))) .thenReturn(mConversationChannel); + + when(mMockINotificationManager.getConsolidatedNotificationPolicy()) + .thenReturn(mock(NotificationManager.Policy.class)); + + when(mBuilder.build()).thenReturn(mock(PriorityOnboardingDialogController.class)); } @Test @@ -240,10 +247,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); @@ -261,10 +269,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); @@ -283,7 +292,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, - null, + mBubbleMetadata, + null, null, null, true); @@ -308,10 +318,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); @@ -331,10 +342,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); @@ -353,10 +365,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); @@ -382,10 +395,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, entry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); @@ -404,13 +418,14 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, (View v, NotificationChannel c, int appUid) -> { assertEquals(mConversationChannel, c); latch.countDown(); }, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -430,10 +445,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); @@ -451,13 +467,14 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); }, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); @@ -476,10 +493,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); View view = mNotificationInfo.findViewById(R.id.silence); @@ -501,10 +519,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); View view = mNotificationInfo.findViewById(R.id.default_behavior); @@ -529,10 +548,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); View view = mNotificationInfo.findViewById(R.id.default_behavior); @@ -556,10 +576,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -596,10 +617,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -635,10 +657,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -675,10 +698,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -709,10 +733,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -741,10 +766,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -774,10 +800,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -807,10 +834,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -839,10 +867,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -870,10 +899,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -892,10 +922,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, + mContext, mBuilderProvider, true); @@ -910,13 +941,12 @@ public class NotificationConversationInfoTest extends SysuiTestCase { // GIVEN the priority onboarding screen is present PriorityOnboardingDialogController.Builder b = - new PriorityOnboardingDialogController.Builder(); + mock(PriorityOnboardingDialogController.Builder.class, Answers.RETURNS_SELF); PriorityOnboardingDialogController controller = mock(PriorityOnboardingDialogController.class); when(b.build()).thenReturn(controller); // GIVEN the user is changing conversation settings - when(mBuilderProvider.get()).thenReturn(b); mNotificationInfo.bindNotification( mShortcutManager, mMockPackageManager, @@ -925,11 +955,12 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, - mBuilderProvider, + mContext, + () -> b, true); // WHEN user clicks "priority" @@ -945,12 +976,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase { Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true); PriorityOnboardingDialogController.Builder b = - new PriorityOnboardingDialogController.Builder(); + mock(PriorityOnboardingDialogController.Builder.class, Answers.RETURNS_SELF); PriorityOnboardingDialogController controller = mock(PriorityOnboardingDialogController.class); when(b.build()).thenReturn(controller); - when(mBuilderProvider.get()).thenReturn(b); mNotificationInfo.bindNotification( mShortcutManager, mMockPackageManager, @@ -959,11 +989,12 @@ public class NotificationConversationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mEntry, + mBubbleMetadata, null, null, mIconFactory, - mUserContext, - mBuilderProvider, + mContext, + () -> b, true); // WHEN user clicks "priority"