Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

Commit

Permalink
For #7249 Create a search widget
Browse files Browse the repository at this point in the history
  • Loading branch information
iorgamgabriel committed Aug 16, 2022
1 parent f46d092 commit 429d294
Show file tree
Hide file tree
Showing 13 changed files with 293 additions and 3 deletions.
54 changes: 54 additions & 0 deletions app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1900,3 +1900,57 @@ metrics:
metadata:
tags:
- Performance
search_widget_installed:
type: boolean
lifetime: application
description: |
Whether or not the search widget is installed
send_in_pings:
- metrics
bugs:
- https://github.com/mozilla-mobile/fenix/issues/
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
metadata:
tags:
- Search

search_widget:
new_tab_button:
type: event
description: |
A user pressed anywhere from the Focus logo until the start of the
microphone icon, opening a new tab search screen.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
metadata:
tags:
- Search
voice_button:
type: event
description: |
A user pressed the microphone icon, opening a new voice search screen.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/
data_sensitivity:
- interaction
notification_emails:
- [email protected]
expires: 119
metadata:
tags:
- Search
18 changes: 18 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@
<activity android:name=".activity.EraseAndOpenShortcutActivity"
android:theme="@android:style/Theme.Translucent" />

<activity
android:name=".searchwidget.VoiceSearchActivity"
android:excludeFromRecents="true"
android:taskAffinity=""
android:theme="@style/Theme.AppCompat.Translucent" />

<provider
android:authorities="${applicationId}.fileprovider"
android:name="androidx.core.content.FileProvider"
Expand Down Expand Up @@ -161,6 +167,18 @@

<meta-data android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" />

<receiver
android:name=".searchwidget.SearchWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/search_widget_info" />
</receiver>

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import mozilla.components.feature.intent.ext.sanitize
import mozilla.components.service.glean.private.NoExtras
import mozilla.components.support.utils.toSafeIntent
import org.mozilla.focus.GleanMetrics.SearchWidget
import org.mozilla.focus.ext.components
import org.mozilla.focus.session.IntentProcessor
import org.mozilla.focus.utils.SupportUtils
Expand All @@ -25,7 +27,9 @@ class IntentReceiverActivity : Activity() {
super.onCreate(savedInstanceState)

val intent = intent.sanitize().toSafeIntent()

if (intent.getBooleanExtra(SEARCH_WIDGET, false)) {
SearchWidget.newTabButton.record(NoExtras())
}
if (intent.dataString.equals(SupportUtils.OPEN_WITH_DEFAULT_BROWSER_URL)) {
dispatchNormalIntent()
return
Expand Down Expand Up @@ -61,4 +65,8 @@ class IntentReceiverActivity : Activity() {

startActivity(intent)
}

companion object {
const val SEARCH_WIDGET = "search_widget"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* 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.focus.searchwidget

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.annotation.VisibleForTesting
import mozilla.components.feature.search.widget.AppSearchWidgetProvider
import mozilla.components.feature.search.widget.BaseVoiceSearchActivity
import mozilla.components.feature.search.widget.SearchWidgetConfig
import mozilla.components.support.utils.PendingIntentUtils
import org.mozilla.focus.R
import org.mozilla.focus.activity.IntentReceiverActivity
import org.mozilla.focus.ext.components

class SearchWidgetProvider : AppSearchWidgetProvider() {

override fun onEnabled(context: Context) {
context.components.settings.addSearchWidgetInstalled(1)
}

override fun onDeleted(context: Context, appWidgetIds: IntArray) {
context.components.settings.addSearchWidgetInstalled(-appWidgetIds.size)
}

override val config: SearchWidgetConfig =
SearchWidgetConfig(
searchWidgetIconResource = R.drawable.ic_splash_screen,
searchWidgetMicrophoneResource = R.drawable.mozac_ic_microphone,
appName = R.string.app_name
)

override fun createTextSearchIntent(context: Context): PendingIntent {
val textSearchIntent = Intent(context, IntentReceiverActivity::class.java)
.apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
this.putExtra(IntentReceiverActivity.SEARCH_WIDGET, true)
}
return PendingIntent.getActivity(
context,
REQUEST_CODE_NEW_TAB,
textSearchIntent,
PendingIntentUtils.defaultFlags or
PendingIntent.FLAG_UPDATE_CURRENT
)
}

override fun shouldShowVoiceSearch(context: Context): Boolean {
return true
}

override fun voiceSearchActivity(): Class<out BaseVoiceSearchActivity> {
return VoiceSearchActivity::class.java
}

companion object {
@VisibleForTesting
const val REQUEST_CODE_NEW_TAB = 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* 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.focus.searchwidget

import android.content.Intent
import mozilla.components.feature.search.widget.BaseVoiceSearchActivity
import mozilla.components.support.locale.LocaleManager
import mozilla.components.support.locale.LocaleManager.getCurrentLocale
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.focus.GleanMetrics.SearchWidget
import org.mozilla.focus.activity.IntentReceiverActivity
import java.util.Locale

class VoiceSearchActivity : BaseVoiceSearchActivity() {

override fun getCurrentLocale(): Locale {
return getCurrentLocale(this)
?: LocaleManager.getSystemDefault()
}

override fun onSpeechRecognitionEnded(spokenText: String) {
val intent = Intent(this, IntentReceiverActivity::class.java)
intent.action = Intent.ACTION_SEND
intent.putExtra(SPEECH_PROCESSING, spokenText)
intent.putExtra(Intent.EXTRA_TEXT, spokenText)
startActivity(intent)
}

override fun onSpeechRecognitionStarted() {
SearchWidget.voiceButton.record(NoExtras())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.mozilla.focus.Components
import org.mozilla.focus.GleanMetrics.Browser
import org.mozilla.focus.GleanMetrics.GleanBuildInfo
import org.mozilla.focus.GleanMetrics.LegacyIds
import org.mozilla.focus.GleanMetrics.Metrics
import org.mozilla.focus.GleanMetrics.MozillaProducts
import org.mozilla.focus.GleanMetrics.Pings
import org.mozilla.focus.GleanMetrics.Preferences
Expand Down Expand Up @@ -75,7 +76,7 @@ class GleanMetricsService(context: Context) : MetricsService {
GlobalScope.launch(IO) {

// Wait for preferences to be collected before we send the activation ping.
collectPrefMetrics(components, settings, context).await()
collectPrefMetricsAsync(components, settings, context).await()

// Set the client ID in Glean as part of the deletion-request.
LegacyIds.clientId.set(UUID.fromString(TelemetryWrapper.clientId))
Expand All @@ -90,7 +91,7 @@ class GleanMetricsService(context: Context) : MetricsService {
}
}

private fun collectPrefMetrics(
private fun collectPrefMetricsAsync(
components: Components,
settings: Settings,
context: Context
Expand All @@ -100,6 +101,8 @@ class GleanMetricsService(context: Context) : MetricsService {
val isFenixDefaultBrowser = FenixProductDetector.isFenixDefaultBrowser(installedBrowsers.defaultBrowser)
val isFocusDefaultBrowser = installedBrowsers.isDefaultBrowser

Metrics.searchWidgetInstalled.set(settings.searchWidgetInstalled)

Browser.isDefault.set(isFocusDefaultBrowser)
Browser.localeOverride.set(components.store.state.locale?.displayName ?: "none")
val shortcutsOnHomeNumber = components.topSitesStorage.getTopSites(
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/org/mozilla/focus/utils/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,20 @@ class Settings(
.commit()
}

fun addSearchWidgetInstalled(count: Int) {
val key = getPreferenceKey(R.string.pref_key_search_widget_installed)
val newValue = preferences.getInt(key, 0) + count
preferences.edit()
.putInt(key, newValue)
.apply()
}

val searchWidgetInstalled: Boolean
get() = 0 < preferences.getInt(
getPreferenceKey(R.string.pref_key_search_widget_installed),
0
)

fun getHttpsOnlyMode(): Engine.HttpsOnlyMode {
return if (preferences.getBoolean(getPreferenceKey(R.string.pref_key_https_only), true)) {
Engine.HttpsOnlyMode.ENABLED
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app/src/main/res/values/preference_keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@

<string name="pref_key_category_security" translatable="false"><xliff:g id="preference_key">security_category</xliff:g></string>

<string name="pref_key_search_widget_installed" translatable="false"><xliff:g id="preference_key">pref_key_search_widget_installed</xliff:g></string>

<string name="pref_key_homescreen_tips" translatable="false">
<xliff:g id="preference_key">use_homescreen_tips</xliff:g>
</string>
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>

<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.NoActionBar">
<item name="android:background">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>

<style name="ItemTextAppearance" parent="TextAppearance.AppCompat.Widget.DropDownItem">
<item name="android:textColor">@color/primaryText</item>
<item name="android:textSize">@dimen/mozac_browser_menu_item_text_size</item>
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/res/xml/search_widget_info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="320dp"
android:minHeight="40dp"
android:resizeMode="horizontal"
android:minResizeWidth="30dp"
android:previewImage="@drawable/focus_search_widget"
android:updatePeriodMillis="3600000"
android:initialLayout="@layout/mozac_search_widget_large"
android:widgetCategory="home_screen">
</appwidget-provider>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* 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.focus.searchwidget

import android.app.PendingIntent
import android.content.Intent
import mozilla.components.support.test.robolectric.testContext
import mozilla.components.support.utils.PendingIntentUtils
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
import org.mozilla.focus.activity.IntentReceiverActivity
import org.mozilla.focus.ext.components
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class SearchWidgetProviderTest {

private lateinit var searchWidgetProvider: SearchWidgetProvider

@Before
fun setup() {
searchWidgetProvider = spy(SearchWidgetProvider())
}

@Test
fun `GIVEN search widget provider WHEN onEnabled is called THEN searchWidgetInstalled from Settings should return true`() {
searchWidgetProvider.onEnabled(testContext)

assertEquals(testContext.components.settings.searchWidgetInstalled, true)
}

@Test
fun `GIVEN search widget provider WHEN onDeleted is called THEN searchWidgetInstalled from Settings should return false`() {
searchWidgetProvider.onDeleted(testContext, intArrayOf(1))

assertEquals(testContext.components.settings.searchWidgetInstalled, false)
}

@Test
fun `GIVEN search widget provider WHEN createTextSearchIntent is called THEN an PendingIntent should be return`() {
val textSearchIntent = Intent(testContext, IntentReceiverActivity::class.java)
.apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
this.putExtra(IntentReceiverActivity.SEARCH_WIDGET, true)
}
val dummyPendingIntent = PendingIntent.getActivity(
testContext,
SearchWidgetProvider.REQUEST_CODE_NEW_TAB,
textSearchIntent,
PendingIntentUtils.defaultFlags or
PendingIntent.FLAG_UPDATE_CURRENT
)

assertEquals(searchWidgetProvider.createTextSearchIntent(testContext), dummyPendingIntent)
}

@Test
fun `GIVEN search widget provider WHEN shouldShowVoiceSearch is called THEN true should be return`() {
assertEquals(searchWidgetProvider.shouldShowVoiceSearch(testContext), true)
}

@Test
fun `GIVEN search widget provider WHEN voiceSearchActivity is called THEN VoiceSearchActivity should be return`() {
assertEquals(searchWidgetProvider.voiceSearchActivity(), VoiceSearchActivity::class.java)
}
}

0 comments on commit 429d294

Please sign in to comment.