diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c196a325..63da54a03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #1312 - Added clear textfield buttons for editing bookmarks - #1312 - Added a missing edit action for bookmark selections - #974 - Added telemetry for bookmarks +- #113 - Added QR code scanner ### Changed - #1429 - Updated site permissions ui for MVP diff --git a/app/build.gradle b/app/build.gradle index 9bc32abf1..29635f62b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -297,6 +297,7 @@ dependencies { implementation Deps.mozilla_feature_downloads implementation Deps.mozilla_feature_intent implementation Deps.mozilla_feature_prompts + implementation Deps.mozilla_feature_qr implementation Deps.mozilla_feature_session implementation Deps.mozilla_feature_sync implementation Deps.mozilla_feature_toolbar diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index 8c7510ad9..e468fc7ae 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -14,12 +14,13 @@ import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.view.* import mozilla.components.browser.search.SearchEngine +import mozilla.components.feature.qr.QrFeature import mozilla.components.feature.search.SearchUseCases import mozilla.components.feature.session.SessionUseCases +import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.kotlin.isUrl import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity -import org.mozilla.fenix.utils.ItsNotBrokenSnack import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.toolbar.SearchAction @@ -42,6 +43,7 @@ class SearchFragment : Fragment() { private lateinit var awesomeBarComponent: AwesomeBarComponent private var sessionId: String? = null private var isPrivate = false + private val qrFeature = ViewBoundFeatureWrapper() override fun onCreateView( inflater: LayoutInflater, @@ -57,10 +59,6 @@ class SearchFragment : Fragment() { } } ?: "" - view.search_scan_button.setOnClickListener { - ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "113") - } - toolbarComponent = ToolbarComponent( view.toolbar_component_wrapper, ActionBusFactory.get(this), @@ -80,6 +78,26 @@ class SearchFragment : Fragment() { layoutComponents(view.search_layout) + qrFeature.set( + QrFeature( + requireContext(), + fragmentManager = requireFragmentManager(), + onNeedToRequestPermissions = { permissions -> + requestPermissions(permissions, REQUEST_CODE_CAMERA_PERMISSIONS) + }, + onScanResult = { result -> + (activity as HomeActivity) + .openToBrowserAndLoad(result, from = BrowserDirection.FromSearch) + // TODO add metrics, also should we have confirmation before going to a URL? + }), + owner = this, + view = view + ) + + view.search_scan_button.setOnClickListener { + qrFeature.get()?.scan(R.id.container) + } + lifecycle.addObserver((toolbarComponent.uiView as ToolbarUIView).toolbarIntegration) view.toolbar_wrapper.clipToOutline = false @@ -114,13 +132,17 @@ class SearchFragment : Fragment() { when (it) { is SearchAction.UrlCommitted -> { if (it.url.isNotBlank()) { - (activity as HomeActivity).openToBrowserAndLoad(it.url, it.session, it.engine, - BrowserDirection.FromSearch) + (activity as HomeActivity).openToBrowserAndLoad( + it.url, it.session, it.engine, + BrowserDirection.FromSearch + ) val event = if (it.url.isUrl()) { Event.EnteredUrl(false) } else { - if (it.engine == null) { return@subscribe } + if (it.engine == null) { + return@subscribe + } createSearchEvent(it.engine, false) } @@ -152,7 +174,9 @@ class SearchFragment : Fragment() { .invoke(it.searchTerms, it.engine) (activity as HomeActivity).openToBrowser(sessionId, BrowserDirection.FromSearch) - if (it.engine == null) { return@subscribe } + if (it.engine == null) { + return@subscribe + } val event = createSearchEvent(it.engine, true) requireComponents.analytics.metrics.track(event) @@ -204,4 +228,8 @@ class SearchFragment : Fragment() { false -> context.components.useCases.tabsUseCases.addTab } } + + companion object { + private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1 + } } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 9bfaf8bea..a399f49ce 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -90,6 +90,7 @@ object Deps { const val mozilla_feature_contextmenu = "org.mozilla.components:feature-contextmenu:${Versions.mozilla_android_components}" const val mozilla_feature_customtabs = "org.mozilla.components:feature-customtabs:${Versions.mozilla_android_components}" const val mozilla_feature_intent = "org.mozilla.components:feature-intent:${Versions.mozilla_android_components}" + const val mozilla_feature_qr = "org.mozilla.components:feature-qr:${Versions.mozilla_android_components}" const val mozilla_feature_search = "org.mozilla.components:feature-search:${Versions.mozilla_android_components}" const val mozilla_feature_session = "org.mozilla.components:feature-session:${Versions.mozilla_android_components}" const val mozilla_feature_sync = "org.mozilla.components:feature-sync:${Versions.mozilla_android_components}"