diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aab865ec7ba1..61a63d2b52b7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -240,7 +240,7 @@ android:name=".crashes.CrashListActivity" android:exported="false" /> - - val currentWidth = appWidgetManager.getAppWidgetOptions(appWidgetId).getInt(OPTION_APPWIDGET_MIN_WIDTH) - val layoutSize = getLayoutSize(currentWidth) - // It's not enough to just hide the microphone on the "small" sized widget due to its design. - // The "small" widget needs a complete redesign, meaning it needs a new layout file. - val showMic = (voiceSearchIntent != null) - val layout = getLayout(layoutSize, showMic) - val text = getText(layoutSize, context) - - val views = createRemoteViews(context, layout, textSearchIntent, voiceSearchIntent, text) - appWidgetManager.updateAppWidget(appWidgetId, views) - } - } - - override fun onAppWidgetOptionsChanged( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetId: Int, - newOptions: Bundle? - ) { - val textSearchIntent = createTextSearchIntent(context) - val voiceSearchIntent = createVoiceSearchIntent(context) - - val currentWidth = appWidgetManager.getAppWidgetOptions(appWidgetId).getInt(OPTION_APPWIDGET_MIN_WIDTH) - val layoutSize = getLayoutSize(currentWidth) - val showMic = (voiceSearchIntent != null) - val layout = getLayout(layoutSize, showMic) - val text = getText(layoutSize, context) - - val views = createRemoteViews(context, layout, textSearchIntent, voiceSearchIntent, text) - appWidgetManager.updateAppWidget(appWidgetId, views) - } + override val config: SearchWidgetConfig = + SearchWidgetConfig( + searchWidgetIconResource = R.drawable.ic_launcher_foreground, + searchWidgetMicrophoneResource = R.drawable.ic_microphone_widget, + appName = R.string.app_name + ) - /** - * Builds pending intent that opens the browser and starts a new text search. - */ - private fun createTextSearchIntent(context: Context): PendingIntent { + override fun createTextSearchIntent(context: Context): PendingIntent { return Intent(context, IntentReceiverActivity::class.java) .let { intent -> val createTextSearchIntentFlags = IntentUtils.defaultIntentPendingFlags or PendingIntent.FLAG_UPDATE_CURRENT intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - intent.putExtra(HomeActivity.OPEN_TO_SEARCH, StartSearchIntentProcessor.SEARCH_WIDGET) + intent.putExtra( + HomeActivity.OPEN_TO_SEARCH, + StartSearchIntentProcessor.SEARCH_WIDGET + ) PendingIntent.getActivity( context, REQUEST_CODE_NEW_TAB, intent, createTextSearchIntentFlags @@ -98,155 +60,15 @@ class SearchWidgetProvider : AppWidgetProvider() { } } - /** - * Builds pending intent that starts a new voice search. - */ - @VisibleForTesting - internal fun createVoiceSearchIntent(context: Context): PendingIntent? { - if (!context.settings().shouldShowVoiceSearch) { - return null - } - - val voiceIntent = Intent(context, VoiceSearchActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - putExtra(SPEECH_PROCESSING, true) - } - - val intentSpeech = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) - - return intentSpeech.resolveActivity(context.packageManager)?.let { - PendingIntent.getActivity( - context, - REQUEST_CODE_VOICE, voiceIntent, IntentUtils.defaultIntentPendingFlags - ) - } + override fun shouldShowVoiceSearch(context: Context): Boolean { + return context.settings().shouldShowVoiceSearch } - private fun createRemoteViews( - context: Context, - layout: Int, - textSearchIntent: PendingIntent, - voiceSearchIntent: PendingIntent?, - text: String? - ): RemoteViews { - return RemoteViews(context.packageName, layout).apply { - setIcon(context) - when (layout) { - R.layout.search_widget_extra_small_v1, - R.layout.search_widget_extra_small_v2, - R.layout.search_widget_small_no_mic -> { - setOnClickPendingIntent(R.id.button_search_widget_new_tab, textSearchIntent) - } - R.layout.search_widget_small -> { - setOnClickPendingIntent(R.id.button_search_widget_new_tab, textSearchIntent) - setOnClickPendingIntent(R.id.button_search_widget_voice, voiceSearchIntent) - } - R.layout.search_widget_medium, - R.layout.search_widget_large -> { - setOnClickPendingIntent(R.id.button_search_widget_new_tab, textSearchIntent) - setOnClickPendingIntent(R.id.button_search_widget_voice, voiceSearchIntent) - setOnClickPendingIntent(R.id.button_search_widget_new_tab_icon, textSearchIntent) - setTextViewText(R.id.button_search_widget_new_tab, text) - // Unlike "small" widget, "medium" and "large" sizes do not have separate layouts - // that exclude the microphone icon, which is why we must hide it accordingly here. - if (voiceSearchIntent == null) { - setViewVisibility(R.id.button_search_widget_voice, View.GONE) - } - } - } - } + override fun voiceSearchActivity(): VoiceSearchActivity { + return FenixVoiceSearchActivity() } - private fun RemoteViews.setIcon(context: Context) { - // gradient color available for android:fillColor only on SDK 24+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - setImageViewResource( - R.id.button_search_widget_new_tab_icon, - R.drawable.ic_launcher_foreground - ) - } else { - setImageViewBitmap( - R.id.button_search_widget_new_tab_icon, - AppCompatResources.getDrawable( - context, - R.drawable.ic_launcher_foreground - )?.toBitmap() - ) - } - - val appName = context.getString(R.string.app_name) - setContentDescription( - R.id.button_search_widget_new_tab_icon, - context.getString(R.string.search_widget_content_description_2, appName) - ) - } - - // Cell sizes obtained from the actual dimensions listed in search widget specs companion object { - private const val DP_EXTRA_SMALL = 64 - private const val DP_SMALL = 100 - private const val DP_MEDIUM = 192 - private const val DP_LARGE = 256 private const val REQUEST_CODE_NEW_TAB = 0 - private const val REQUEST_CODE_VOICE = 1 - - fun updateAllWidgets(context: Context) { - val widgetManager = AppWidgetManager.getInstance(context) - val widgetIds = widgetManager.getAppWidgetIds(ComponentName(context, SearchWidgetProvider::class.java)) - - if (widgetIds.isNotEmpty()) { - context.sendBroadcast( - Intent(context, SearchWidgetProvider::class.java).apply { - action = AppWidgetManager.ACTION_APPWIDGET_UPDATE - putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds) - } - ) - } - } - - @VisibleForTesting - internal fun getLayoutSize(@Dimension(unit = DP) dp: Int) = when { - dp >= DP_LARGE -> SearchWidgetProviderSize.LARGE - dp >= DP_MEDIUM -> SearchWidgetProviderSize.MEDIUM - dp >= DP_SMALL -> SearchWidgetProviderSize.SMALL - dp >= DP_EXTRA_SMALL -> SearchWidgetProviderSize.EXTRA_SMALL_V2 - else -> SearchWidgetProviderSize.EXTRA_SMALL_V1 - } - - /** - * Get the layout resource to use for the search widget. - */ - @VisibleForTesting - internal fun getLayout(size: SearchWidgetProviderSize, showMic: Boolean) = when (size) { - SearchWidgetProviderSize.LARGE -> R.layout.search_widget_large - SearchWidgetProviderSize.MEDIUM -> R.layout.search_widget_medium - SearchWidgetProviderSize.SMALL -> { - if (showMic) { - R.layout.search_widget_small - } else { - R.layout.search_widget_small_no_mic - } - } - SearchWidgetProviderSize.EXTRA_SMALL_V2 -> R.layout.search_widget_extra_small_v2 - SearchWidgetProviderSize.EXTRA_SMALL_V1 -> R.layout.search_widget_extra_small_v1 - } - - /** - * Get the text to place in the search widget - */ - @VisibleForTesting - internal fun getText(layout: SearchWidgetProviderSize, context: Context) = when (layout) { - SearchWidgetProviderSize.MEDIUM -> context.getString(R.string.search_widget_text_short) - SearchWidgetProviderSize.LARGE -> context.getString(R.string.search_widget_text_long) - else -> null - } } } - -enum class SearchWidgetProviderSize { - EXTRA_SMALL_V1, - EXTRA_SMALL_V2, - SMALL, - MEDIUM, - LARGE, -} diff --git a/app/src/main/res/drawable/ic_microphone_widget_padded.xml b/app/src/main/res/drawable/ic_microphone_widget_padded.xml deleted file mode 100644 index 665b13f1baf0..000000000000 --- a/app/src/main/res/drawable/ic_microphone_widget_padded.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/app/src/main/res/drawable/rounded_search_widget_background.xml b/app/src/main/res/drawable/rounded_search_widget_background.xml deleted file mode 100644 index 4cd3c134377d..000000000000 --- a/app/src/main/res/drawable/rounded_search_widget_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/search_widget_extra_small_v1.xml b/app/src/main/res/layout/search_widget_extra_small_v1.xml deleted file mode 100644 index 0362d1392847..000000000000 --- a/app/src/main/res/layout/search_widget_extra_small_v1.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/search_widget_extra_small_v2.xml b/app/src/main/res/layout/search_widget_extra_small_v2.xml deleted file mode 100644 index 6bac1cbcfe4a..000000000000 --- a/app/src/main/res/layout/search_widget_extra_small_v2.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/search_widget_large.xml b/app/src/main/res/layout/search_widget_large.xml deleted file mode 100644 index e17529759bf9..000000000000 --- a/app/src/main/res/layout/search_widget_large.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/search_widget_medium.xml b/app/src/main/res/layout/search_widget_medium.xml deleted file mode 100644 index c51ff6b61372..000000000000 --- a/app/src/main/res/layout/search_widget_medium.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/search_widget_small.xml b/app/src/main/res/layout/search_widget_small.xml deleted file mode 100644 index db0531accb05..000000000000 --- a/app/src/main/res/layout/search_widget_small.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/search_widget_small_no_mic.xml b/app/src/main/res/layout/search_widget_small_no_mic.xml deleted file mode 100644 index d1ae6e58d3fa..000000000000 --- a/app/src/main/res/layout/search_widget_small_no_mic.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/app/src/main/res/xml/search_widget_info.xml b/app/src/main/res/xml/search_widget_info.xml index ce7e44a2a4c7..bb40d415198d 100644 --- a/app/src/main/res/xml/search_widget_info.xml +++ b/app/src/main/res/xml/search_widget_info.xml @@ -10,6 +10,6 @@ android:minResizeWidth="30dp" android:previewImage="@drawable/fenix_search_widget" android:updatePeriodMillis="3600000" - android:initialLayout="@layout/search_widget_large" + android:initialLayout="@layout/mozac_search_widget_large" android:widgetCategory="home_screen"> diff --git a/app/src/test/java/org/mozilla/fenix/home/intent/SpeechProcessingIntentProcessorTest.kt b/app/src/test/java/org/mozilla/fenix/home/intent/SpeechProcessingIntentProcessorTest.kt index f9e9b6ee5c35..3545f112842d 100644 --- a/app/src/test/java/org/mozilla/fenix/home/intent/SpeechProcessingIntentProcessorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/intent/SpeechProcessingIntentProcessorTest.kt @@ -15,6 +15,7 @@ import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.SearchState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.feature.search.ext.createSearchEngine +import mozilla.components.feature.search.widget.VoiceSearchActivity.Companion.SPEECH_PROCESSING import mozilla.components.support.test.robolectric.testContext import org.junit.Before import org.junit.Test @@ -24,7 +25,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.perf.TestStrictModeManager -import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_PROCESSING @RunWith(FenixRobolectricTestRunner::class) class SpeechProcessingIntentProcessorTest { diff --git a/app/src/test/java/org/mozilla/fenix/settings/search/SearchEngineFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/settings/search/SearchEngineFragmentTest.kt index c69f4b9fb71d..ac954f6e4367 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/search/SearchEngineFragmentTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/search/SearchEngineFragmentTest.kt @@ -99,7 +99,6 @@ class SearchEngineFragmentTest { voiceSearchPreference.callChangeListener(true) verify { preferencesEditor.putBoolean(voiceSearchPreferenceKey, true) } - verify { SearchWidgetProvider.updateAllWidgets(testContext) } } finally { unmockkObject(SearchWidgetProvider.Companion) } diff --git a/app/src/test/java/org/mozilla/fenix/widget/SearchWidgetProviderTest.kt b/app/src/test/java/org/mozilla/fenix/widget/SearchWidgetProviderTest.kt index 6244dc55cf76..7576f8b3f26f 100644 --- a/app/src/test/java/org/mozilla/fenix/widget/SearchWidgetProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/widget/SearchWidgetProviderTest.kt @@ -16,131 +16,16 @@ import io.mockk.mockkStatic import io.mockk.slot import io.mockk.unmockkStatic import io.mockk.verify +import mozilla.components.feature.search.widget.AppSearchWidgetProvider import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import org.junit.Test import org.junit.runner.RunWith -import org.mozilla.fenix.R -import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.gecko.search.SearchWidgetProvider -import org.mozilla.gecko.search.SearchWidgetProviderSize @RunWith(FenixRobolectricTestRunner::class) class SearchWidgetProviderTest { - @Test - fun testGetLayoutSize() { - val sizes = mapOf( - 0 to SearchWidgetProviderSize.EXTRA_SMALL_V1, - 10 to SearchWidgetProviderSize.EXTRA_SMALL_V1, - 63 to SearchWidgetProviderSize.EXTRA_SMALL_V1, - 64 to SearchWidgetProviderSize.EXTRA_SMALL_V2, - 99 to SearchWidgetProviderSize.EXTRA_SMALL_V2, - 100 to SearchWidgetProviderSize.SMALL, - 191 to SearchWidgetProviderSize.SMALL, - 192 to SearchWidgetProviderSize.MEDIUM, - 255 to SearchWidgetProviderSize.MEDIUM, - 256 to SearchWidgetProviderSize.LARGE, - 1000 to SearchWidgetProviderSize.LARGE - ) - - for ((dp, layoutSize) in sizes) { - assertEquals(layoutSize, SearchWidgetProvider.getLayoutSize(dp)) - } - } - - @Test - fun testGetLargeLayout() { - assertEquals( - R.layout.search_widget_large, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.LARGE, showMic = false) - ) - assertEquals( - R.layout.search_widget_large, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.LARGE, showMic = true) - ) - } - - @Test - fun testGetMediumLayout() { - assertEquals( - R.layout.search_widget_medium, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.MEDIUM, showMic = false) - ) - assertEquals( - R.layout.search_widget_medium, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.MEDIUM, showMic = true) - ) - } - - @Test - fun testGetSmallLayout() { - assertEquals( - R.layout.search_widget_small_no_mic, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.SMALL, showMic = false) - ) - assertEquals( - R.layout.search_widget_small, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.SMALL, showMic = true) - ) - } - - @Test - fun testGetExtraSmall2Layout() { - assertEquals( - R.layout.search_widget_extra_small_v2, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.EXTRA_SMALL_V2, showMic = false) - ) - assertEquals( - R.layout.search_widget_extra_small_v2, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.EXTRA_SMALL_V2, showMic = true) - ) - } - - @Test - fun testGetExtraSmall1Layout() { - assertEquals( - R.layout.search_widget_extra_small_v1, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.EXTRA_SMALL_V1, showMic = false) - ) - assertEquals( - R.layout.search_widget_extra_small_v1, - SearchWidgetProvider.getLayout(SearchWidgetProviderSize.EXTRA_SMALL_V1, showMic = true) - ) - } - - @Test - fun testGetText() { - val context = mockk() - every { context.getString(R.string.search_widget_text_short) } returns "Search" - every { context.getString(R.string.search_widget_text_long) } returns "Search the web" - - assertEquals( - "Search the web", - SearchWidgetProvider.getText(SearchWidgetProviderSize.LARGE, context) - ) - assertEquals( - "Search", - SearchWidgetProvider.getText(SearchWidgetProviderSize.MEDIUM, context) - ) - assertNull(SearchWidgetProvider.getText(SearchWidgetProviderSize.SMALL, context)) - assertNull(SearchWidgetProvider.getText(SearchWidgetProviderSize.EXTRA_SMALL_V1, context)) - assertNull(SearchWidgetProvider.getText(SearchWidgetProviderSize.EXTRA_SMALL_V2, context)) - } - - @Test - fun `GIVEN voice search is disabled WHEN createVoiceSearchIntent is called THEN it returns null`() { - val widgetProvider = SearchWidgetProvider() - val context: Context = mockk { - every { settings().shouldShowVoiceSearch } returns false - } - - val result = widgetProvider.createVoiceSearchIntent(context) - - assertNull(result) - } - @Test fun `GIVEN widgets set on screen shown WHEN updateAllWidgets is called THEN it sends a broadcast to update all widgets`() { try { @@ -154,7 +39,7 @@ class SearchWidgetProviderTest { val intentCaptor = slot() every { context.sendBroadcast(capture(intentCaptor)) } just Runs - SearchWidgetProvider.updateAllWidgets(context) + AppSearchWidgetProvider.updateAllWidgets(context, SearchWidgetProvider()) verify { context.sendBroadcast(any()) } assertEquals(SearchWidgetProvider::class.java.name, componentNameCaptor.captured.className) @@ -179,7 +64,7 @@ class SearchWidgetProviderTest { val intentCaptor = slot() every { context.sendBroadcast(capture(intentCaptor)) } just Runs - SearchWidgetProvider.updateAllWidgets(context) + AppSearchWidgetProvider.updateAllWidgets(context, SearchWidgetProvider()) verify(exactly = 0) { context.sendBroadcast(any()) } } finally { diff --git a/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt b/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt deleted file mode 100644 index cb8fd76f1ba3..000000000000 --- a/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.widget - -import android.app.Activity -import android.content.ComponentName -import android.content.Intent -import android.content.IntentFilter -import android.os.Bundle -import android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH -import android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL -import android.speech.RecognizerIntent.EXTRA_RESULTS -import android.speech.RecognizerIntent.LANGUAGE_MODEL_FREE_FORM -import androidx.appcompat.app.AppCompatActivity.RESULT_OK -import androidx.test.core.app.ApplicationProvider -import io.mockk.every -import io.mockk.mockk -import mozilla.components.support.test.robolectric.testContext -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.FenixApplication -import org.mozilla.fenix.HomeActivity.Companion.OPEN_TO_BROWSER_AND_LOAD -import org.mozilla.fenix.IntentReceiverActivity -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner -import org.mozilla.fenix.helpers.perf.TestStrictModeManager -import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.PREVIOUS_INTENT -import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_PROCESSING -import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_REQUEST_CODE -import org.robolectric.Robolectric -import org.robolectric.Shadows.shadowOf -import org.robolectric.android.controller.ActivityController -import org.robolectric.shadows.ShadowActivity - -@RunWith(FenixRobolectricTestRunner::class) -class VoiceSearchActivityTest { - - private lateinit var controller: ActivityController - private lateinit var activity: VoiceSearchActivity - private lateinit var shadow: ShadowActivity - - @Before - fun setup() { - val intent = Intent() - intent.putExtra(SPEECH_PROCESSING, true) - - controller = Robolectric.buildActivity(VoiceSearchActivity::class.java, intent) - activity = controller.get() - shadow = shadowOf(activity) - } - - private fun allowVoiceIntentToResolveActivity() { - val context = ApplicationProvider.getApplicationContext() - val shadowPackageManager = shadowOf(context.packageManager) - val component = ComponentName("com.test", "Test") - shadowPackageManager.addActivityIfNotPresent(component) - shadowPackageManager.addIntentFilterForActivity( - component, - IntentFilter(ACTION_RECOGNIZE_SPEECH).apply { addCategory(Intent.CATEGORY_DEFAULT) } - ) - } - - @Test - fun `process intent with speech processing set to true`() { - every { testContext.components.analytics } returns mockk(relaxed = true) - every { testContext.components.strictMode } returns TestStrictModeManager() - allowVoiceIntentToResolveActivity() - controller.create() - - val intentForResult = shadow.peekNextStartedActivityForResult() - assertEquals(SPEECH_REQUEST_CODE, intentForResult.requestCode) - assertEquals(ACTION_RECOGNIZE_SPEECH, intentForResult.intent.action) - assertEquals( - LANGUAGE_MODEL_FREE_FORM, - intentForResult.intent.getStringExtra(EXTRA_LANGUAGE_MODEL) - ) - } - - @Test - fun `process intent with speech processing set to false`() { - allowVoiceIntentToResolveActivity() - val intent = Intent() - intent.putExtra(SPEECH_PROCESSING, false) - - val controller = Robolectric.buildActivity(VoiceSearchActivity::class.java, intent) - val activity = controller.get() - - controller.create() - - assertTrue(activity.isFinishing) - } - - @Test - fun `process null intent`() { - allowVoiceIntentToResolveActivity() - val controller = Robolectric.buildActivity(VoiceSearchActivity::class.java, null) - val activity = controller.get() - - controller.create() - - assertTrue(activity.isFinishing) - } - - @Test - fun `save previous intent to instance state`() { - allowVoiceIntentToResolveActivity() - val previousIntent = Intent().apply { - putExtra(SPEECH_PROCESSING, true) - } - val savedInstanceState = Bundle().apply { - putParcelable(PREVIOUS_INTENT, previousIntent) - } - val outState = Bundle() - - controller.create(savedInstanceState) - controller.saveInstanceState(outState) - - assertEquals(previousIntent, outState.getParcelable(PREVIOUS_INTENT)) - } - - @Test - fun `process intent with speech processing in previous intent set to true`() { - allowVoiceIntentToResolveActivity() - val savedInstanceState = Bundle() - val previousIntent = Intent().apply { - putExtra(SPEECH_PROCESSING, true) - } - savedInstanceState.putParcelable(PREVIOUS_INTENT, previousIntent) - - controller.create(savedInstanceState) - - assertFalse(activity.isFinishing) - assertNull(shadow.peekNextStartedActivityForResult()) - } - - @Test - fun `handle speech result`() { - every { testContext.components.analytics } returns mockk(relaxed = true) - every { testContext.components.strictMode } returns TestStrictModeManager() - allowVoiceIntentToResolveActivity() - controller.create() - - val resultIntent = Intent().apply { - putStringArrayListExtra(EXTRA_RESULTS, arrayListOf("hello world")) - } - shadow.receiveResult( - shadow.peekNextStartedActivityForResult().intent, - RESULT_OK, - resultIntent - ) - - val browserIntent = shadow.peekNextStartedActivity() - - assertTrue(activity.isFinishing) - assertEquals( - ComponentName(activity, IntentReceiverActivity::class.java), - browserIntent.component - ) - assertEquals("hello world", browserIntent.getStringExtra(SPEECH_PROCESSING)) - assertTrue(browserIntent.getBooleanExtra(OPEN_TO_BROWSER_AND_LOAD, false)) - } - - @Test - fun `handle invalid result code`() { - every { testContext.components.analytics } returns mockk(relaxed = true) - every { testContext.components.strictMode } returns TestStrictModeManager() - allowVoiceIntentToResolveActivity() - controller.create() - - val resultIntent = Intent() - shadow.receiveResult( - shadow.peekNextStartedActivityForResult().intent, - Activity.RESULT_CANCELED, - resultIntent - ) - - assertTrue(activity.isFinishing) - } - - @Test - fun `handle no activity able to resolve voice intent`() { - controller.create() - assertTrue(activity.isFinishing) - } -}