Loading packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt +81 −3 Original line number Diff line number Diff line Loading @@ -15,14 +15,32 @@ */ package com.android.systemui.globalactions import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.res.Configuration import android.net.Uri import android.service.quickaccesswallet.QuickAccessWalletClient import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView import com.android.systemui.R import com.android.systemui.controls.controller.ControlsController import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject // Empty class, will be overridden for relevant devices private const val TAG = "GlobalActionsInfo" /** Maximum number of times to show change info message */ private const val MAX_VIEW_COUNT = 3 /** Maximum number of buttons allowed in landscape before this panel does not fit */ private const val MAX_BUTTONS_LANDSCAPE = 4 private const val PREFERENCE = "global_actions_info_prefs" private const val KEY_VIEW_COUNT = "view_count" class GlobalActionsInfoProvider @Inject constructor( private val context: Context, private val walletClient: QuickAccessWalletClient, Loading @@ -30,9 +48,69 @@ class GlobalActionsInfoProvider @Inject constructor( private val activityStarter: ActivityStarter ) { fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) { } private var pendingIntent: PendingIntent init { val url = context.resources.getString(R.string.global_actions_change_url) val intent = Intent(Intent.ACTION_VIEW).apply { setData(Uri.parse(url)) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) } fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) { // This panel does not fit on landscape with two rows of buttons showing, // so skip adding the panel (and incrementing view counT) in that case val isLandscape = context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) { return } val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel, parent, false) val button = view.findViewById<ImageView>(R.id.global_actions_change_button) button.setOnClickListener { _ -> dismissParent.run() activityStarter.postStartActivityDismissingKeyguard(pendingIntent) } parent.addView(view, 0) // Add to top incrementViewCount() } fun shouldShowMessage(): Boolean { // This is only relevant for some devices val isEligible = context.resources.getBoolean( R.bool.global_actions_show_change_info) if (!isEligible) { return false } val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) // Only show to users who previously had these items set up val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) { sharedPrefs.getInt(KEY_VIEW_COUNT, 0) } else { -1 } // Limit number of times this is displayed return viewCount > -1 && viewCount < MAX_VIEW_COUNT } private fun hadContent(): Boolean { // Check whether user would have seen content in the power menu that has now moved val hadControls = controlsController.getFavorites().size > 0 val hadCards = walletClient.isWalletFeatureAvailable Log.d(TAG, "Previously had controls $hadControls, cards $hadCards") return hadControls || hadCards } private fun incrementViewCount() { val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0) sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply() } } No newline at end of file packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt +76 −15 Original line number Diff line number Diff line Loading @@ -14,45 +14,75 @@ * limitations under the License. */ package com.android.systemui.globalactions package com.google.android.systemui.globalactions import android.content.Context import android.content.SharedPreferences import android.content.res.Configuration import android.content.res.Resources import android.service.quickaccesswallet.QuickAccessWalletClient import android.testing.AndroidTestingRunner import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.R import com.android.systemui.controls.controller.ControlsController import com.android.systemui.globalactions.GlobalActionsInfoProvider import com.android.systemui.plugins.ActivityStarter import com.google.common.truth.Truth.assertThat import com.android.systemui.SysuiTestCase import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyObject import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever private const val PREFERENCE = "global_actions_info_prefs" private const val KEY_VIEW_COUNT = "view_count" private fun <T> eq(value: T): T = Mockito.eq(value) ?: value @SmallTest @RunWith(AndroidTestingRunner::class) class GlobalActionsInfoProviderTest : SysuiTestCase() { @Mock private lateinit var walletClient: QuickAccessWalletClient @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var walletClient: QuickAccessWalletClient @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var mockContext: Context @Mock private lateinit var mockResources: Resources @Mock private lateinit var sharedPrefs: SharedPreferences @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor private lateinit var infoProvider: GlobalActionsInfoProvider @Before fun setup() { MockitoAnnotations.initMocks(this) mockContext = spy(context) mockResources = spy(context.resources) whenever(mockContext.resources).thenReturn(mockResources) whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) .thenReturn(true) whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt())) .thenReturn(sharedPrefs) whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor) whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor) whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean())) .thenReturn(sharedPrefsEditor) infoProvider = GlobalActionsInfoProvider( context, mockContext, walletClient, controlsController, activityStarter Loading @@ -60,14 +90,45 @@ class GlobalActionsInfoProviderTest : SysuiTestCase() { } @Test fun testAddPanel_doesNothing() { fun testIsEligible_noCards() { whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) whenever(walletClient.isWalletFeatureAvailable).thenReturn(false) assertFalse(infoProvider.shouldShowMessage()) } @Test fun testIsEligible_hasCards() { whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) whenever(walletClient.isWalletFeatureAvailable).thenReturn(true) assertTrue(infoProvider.shouldShowMessage()) } @Test fun testNotEligible_shouldNotShow() { whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) .thenReturn(false) assertFalse(infoProvider.shouldShowMessage()) } @Test fun testTooManyButtons_doesNotAdd() { val configuration = Configuration() configuration.orientation = Configuration.ORIENTATION_LANDSCAPE whenever(mockResources.configuration).thenReturn(configuration) val parent = mock(ViewGroup::class.java) infoProvider.addPanel(context, parent, 1, { }) infoProvider.addPanel(mockContext, parent, 5, { }) verify(parent, never()).addView(anyObject(), anyInt()) } @Test fun testShouldShowPanel() { assertThat(infoProvider.shouldShowMessage()).isFalse() fun testLimitTimesShown() { whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4) assertFalse(infoProvider.shouldShowMessage()) } } No newline at end of file Loading
packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt +81 −3 Original line number Diff line number Diff line Loading @@ -15,14 +15,32 @@ */ package com.android.systemui.globalactions import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.res.Configuration import android.net.Uri import android.service.quickaccesswallet.QuickAccessWalletClient import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView import com.android.systemui.R import com.android.systemui.controls.controller.ControlsController import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject // Empty class, will be overridden for relevant devices private const val TAG = "GlobalActionsInfo" /** Maximum number of times to show change info message */ private const val MAX_VIEW_COUNT = 3 /** Maximum number of buttons allowed in landscape before this panel does not fit */ private const val MAX_BUTTONS_LANDSCAPE = 4 private const val PREFERENCE = "global_actions_info_prefs" private const val KEY_VIEW_COUNT = "view_count" class GlobalActionsInfoProvider @Inject constructor( private val context: Context, private val walletClient: QuickAccessWalletClient, Loading @@ -30,9 +48,69 @@ class GlobalActionsInfoProvider @Inject constructor( private val activityStarter: ActivityStarter ) { fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) { } private var pendingIntent: PendingIntent init { val url = context.resources.getString(R.string.global_actions_change_url) val intent = Intent(Intent.ACTION_VIEW).apply { setData(Uri.parse(url)) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) } fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) { // This panel does not fit on landscape with two rows of buttons showing, // so skip adding the panel (and incrementing view counT) in that case val isLandscape = context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) { return } val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel, parent, false) val button = view.findViewById<ImageView>(R.id.global_actions_change_button) button.setOnClickListener { _ -> dismissParent.run() activityStarter.postStartActivityDismissingKeyguard(pendingIntent) } parent.addView(view, 0) // Add to top incrementViewCount() } fun shouldShowMessage(): Boolean { // This is only relevant for some devices val isEligible = context.resources.getBoolean( R.bool.global_actions_show_change_info) if (!isEligible) { return false } val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) // Only show to users who previously had these items set up val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) { sharedPrefs.getInt(KEY_VIEW_COUNT, 0) } else { -1 } // Limit number of times this is displayed return viewCount > -1 && viewCount < MAX_VIEW_COUNT } private fun hadContent(): Boolean { // Check whether user would have seen content in the power menu that has now moved val hadControls = controlsController.getFavorites().size > 0 val hadCards = walletClient.isWalletFeatureAvailable Log.d(TAG, "Previously had controls $hadControls, cards $hadCards") return hadControls || hadCards } private fun incrementViewCount() { val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0) sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply() } } No newline at end of file
packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt +76 −15 Original line number Diff line number Diff line Loading @@ -14,45 +14,75 @@ * limitations under the License. */ package com.android.systemui.globalactions package com.google.android.systemui.globalactions import android.content.Context import android.content.SharedPreferences import android.content.res.Configuration import android.content.res.Resources import android.service.quickaccesswallet.QuickAccessWalletClient import android.testing.AndroidTestingRunner import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.R import com.android.systemui.controls.controller.ControlsController import com.android.systemui.globalactions.GlobalActionsInfoProvider import com.android.systemui.plugins.ActivityStarter import com.google.common.truth.Truth.assertThat import com.android.systemui.SysuiTestCase import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyObject import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever private const val PREFERENCE = "global_actions_info_prefs" private const val KEY_VIEW_COUNT = "view_count" private fun <T> eq(value: T): T = Mockito.eq(value) ?: value @SmallTest @RunWith(AndroidTestingRunner::class) class GlobalActionsInfoProviderTest : SysuiTestCase() { @Mock private lateinit var walletClient: QuickAccessWalletClient @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var walletClient: QuickAccessWalletClient @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var mockContext: Context @Mock private lateinit var mockResources: Resources @Mock private lateinit var sharedPrefs: SharedPreferences @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor private lateinit var infoProvider: GlobalActionsInfoProvider @Before fun setup() { MockitoAnnotations.initMocks(this) mockContext = spy(context) mockResources = spy(context.resources) whenever(mockContext.resources).thenReturn(mockResources) whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) .thenReturn(true) whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt())) .thenReturn(sharedPrefs) whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor) whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor) whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean())) .thenReturn(sharedPrefsEditor) infoProvider = GlobalActionsInfoProvider( context, mockContext, walletClient, controlsController, activityStarter Loading @@ -60,14 +90,45 @@ class GlobalActionsInfoProviderTest : SysuiTestCase() { } @Test fun testAddPanel_doesNothing() { fun testIsEligible_noCards() { whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) whenever(walletClient.isWalletFeatureAvailable).thenReturn(false) assertFalse(infoProvider.shouldShowMessage()) } @Test fun testIsEligible_hasCards() { whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) whenever(walletClient.isWalletFeatureAvailable).thenReturn(true) assertTrue(infoProvider.shouldShowMessage()) } @Test fun testNotEligible_shouldNotShow() { whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) .thenReturn(false) assertFalse(infoProvider.shouldShowMessage()) } @Test fun testTooManyButtons_doesNotAdd() { val configuration = Configuration() configuration.orientation = Configuration.ORIENTATION_LANDSCAPE whenever(mockResources.configuration).thenReturn(configuration) val parent = mock(ViewGroup::class.java) infoProvider.addPanel(context, parent, 1, { }) infoProvider.addPanel(mockContext, parent, 5, { }) verify(parent, never()).addView(anyObject(), anyInt()) } @Test fun testShouldShowPanel() { assertThat(infoProvider.shouldShowMessage()).isFalse() fun testLimitTimesShown() { whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4) assertFalse(infoProvider.shouldShowMessage()) } } No newline at end of file