Merge pull request #431 from meshtastic/mlkit-scan

add mlkit barcode scanner
pull/433/head
Andre Kirchhoff 2022-05-17 17:42:26 -03:00 zatwierdzone przez GitHub
commit 747ce82a7b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 100 dodań i 34 usunięć

Wyświetl plik

@ -173,9 +173,10 @@ dependencies {
// location services // location services
implementation 'com.google.android.gms:play-services-location:19.0.1' implementation 'com.google.android.gms:play-services-location:19.0.1'
// For Google Sign-In (owner name accesss) // For Google Sign-In (owner name accesss)
implementation 'com.google.android.gms:play-services-auth:20.1.0' implementation 'com.google.android.gms:play-services-auth:20.1.0'
// ML Kit barcode scanning
implementation 'com.google.android.gms:play-services-code-scanner:16.0.0-beta1'
// Add the Firebase SDK for Crashlytics. // Add the Firebase SDK for Crashlytics.
implementation 'com.google.firebase:firebase-crashlytics:18.2.6' implementation 'com.google.firebase:firebase-crashlytics:18.2.6'

Wyświetl plik

@ -97,6 +97,9 @@
<meta-data <meta-data
android:name="firebase_analytics_collection_enabled" android:name="firebase_analytics_collection_enabled"
android:value="false" /> android:value="false" />
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode_ui"/>
<!-- we need bind job service for oreo --> <!-- we need bind job service for oreo -->
<service <service

Wyświetl plik

@ -518,8 +518,7 @@ class MainActivity : BaseActivity(), Logging,
requestedChannelUrl = appLinkData requestedChannelUrl = appLinkData
// if the device is connected already, process it now // if the device is connected already, process it now
if (model.isConnected()) perhapsChangeChannel()
perhapsChangeChannel()
// We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel // We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel
} }
@ -721,16 +720,16 @@ class MainActivity : BaseActivity(), Logging,
} }
} }
fun perhapsChangeChannel(url: Uri? = requestedChannelUrl) { private fun perhapsChangeChannel(url: Uri? = requestedChannelUrl) {
// If the is opening a channel URL, handle it now // if the device is connected already, process it now
if (url != null) { if (url != null && model.isConnected()) {
requestedChannelUrl = null
try { try {
val channels = ChannelSet(url) val channels = ChannelSet(url)
val primary = channels.primaryChannel val primary = channels.primaryChannel
if (primary == null) if (primary == null)
showSnackbar(R.string.channel_invalid) showSnackbar(R.string.channel_invalid)
else { else {
requestedChannelUrl = null
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.new_channel_rcvd) .setTitle(R.string.new_channel_rcvd)
@ -967,6 +966,15 @@ class MainActivity : BaseActivity(), Logging,
} }
} }
// Call perhapsChangeChannel() whenever [changeChannelUrl] updates with a non-null value
model.requestChannelUrl.observe(this) { url ->
url?.let {
requestedChannelUrl = url
model.clearRequestChannelUrl()
perhapsChangeChannel()
}
}
try { try {
bindMeshService() bindMeshService()
} catch (ex: BindFailedException) { } catch (ex: BindFailedException) {

Wyświetl plik

@ -107,6 +107,20 @@ class UIViewModel @Inject constructor(
private val _channels = MutableLiveData<ChannelSet?>() private val _channels = MutableLiveData<ChannelSet?>()
val channels: LiveData<ChannelSet?> get() = _channels val channels: LiveData<ChannelSet?> get() = _channels
private val _requestChannelUrl = MutableLiveData<Uri?>(null)
val requestChannelUrl: LiveData<Uri?> get() = _requestChannelUrl
fun setRequestChannelUrl(channelUrl: Uri) {
_requestChannelUrl.value = channelUrl
}
/**
* Called immediately after activity observes requestChannelUrl
*/
fun clearRequestChannelUrl() {
_requestChannelUrl.value = null
}
var positionBroadcastSecs: Int? var positionBroadcastSecs: Int?
get() { get() {
_radioConfig.value?.preferences?.let { _radioConfig.value?.preferences?.let {

Wyświetl plik

@ -1,5 +1,6 @@
package com.geeksville.mesh.ui package com.geeksville.mesh.ui
import android.Manifest
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.graphics.ColorMatrix import android.graphics.ColorMatrix
@ -13,15 +14,15 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import com.geeksville.analytics.DataPair import com.geeksville.analytics.DataPair
import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging import com.geeksville.android.Logging
import com.geeksville.android.hideKeyboard import com.geeksville.android.hideKeyboard
import com.geeksville.android.isGooglePlayAvailable
import com.geeksville.mesh.AppOnlyProtos import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.ChannelProtos import com.geeksville.mesh.ChannelProtos
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.android.hasCameraPermission import com.geeksville.mesh.android.hasCameraPermission
import com.geeksville.mesh.databinding.ChannelFragmentBinding import com.geeksville.mesh.databinding.ChannelFragmentBinding
@ -31,9 +32,11 @@ import com.geeksville.mesh.model.ChannelSet
import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.model.UIViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions import com.journeyapps.barcodescanner.ScanOptions
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import java.security.SecureRandom import java.security.SecureRandom
@ -189,6 +192,52 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
} }
} }
private fun zxingScan() {
debug("Starting zxing QR code scanner")
val zxingScan = ScanOptions()
zxingScan.setCameraId(0)
zxingScan.setPrompt("")
zxingScan.setBeepEnabled(false)
zxingScan.setDesiredBarcodeFormats(ScanOptions.QR_CODE)
barcodeLauncher.launch(zxingScan)
}
private fun requestPermissionAndScan() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.camera_required)
.setMessage(R.string.why_camera_required)
.setNeutralButton(R.string.cancel) { _, _ ->
debug("Camera permission denied")
}
.setPositiveButton(getString(R.string.accept)) { _, _ ->
requestPermissionAndScanLauncher.launch(Manifest.permission.CAMERA)
}
.show()
}
private fun mlkitScan() {
debug("Starting ML Kit QR code scanner")
val options = GmsBarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE
)
.build()
val scanner = GmsBarcodeScanning.getClient(requireContext(), options)
scanner.startScan()
.addOnSuccessListener { barcode ->
if (barcode.rawValue != null)
model.setRequestChannelUrl(Uri.parse(barcode.rawValue))
}
.addOnFailureListener {
Snackbar.make(
requireView(),
R.string.channel_invalid,
Snackbar.LENGTH_SHORT
).show()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -212,25 +261,14 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
} }
binding.scanButton.setOnClickListener { binding.scanButton.setOnClickListener {
if ((requireActivity() as MainActivity).hasCameraPermission()) { if (isGooglePlayAvailable(requireContext())) {
debug("Starting QR code scanner") mlkitScan()
val zxingScan = ScanOptions()
zxingScan.setCameraId(0)
zxingScan.setPrompt("")
zxingScan.setBeepEnabled(false)
zxingScan.setDesiredBarcodeFormats(ScanOptions.QR_CODE)
barcodeLauncher.launch(zxingScan)
} else { } else {
MaterialAlertDialogBuilder(requireContext()) if (requireContext().hasCameraPermission()) {
.setTitle(R.string.camera_required) zxingScan()
.setMessage(R.string.why_camera_required) } else {
.setNeutralButton(R.string.cancel) { _, _ -> requestPermissionAndScan()
debug("Camera permission denied") }
}
.setPositiveButton(getString(R.string.accept)) { _, _ ->
(requireActivity() as MainActivity).requestCameraPermission()
}
.show()
} }
} }
@ -315,16 +353,18 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
if (getString(item.configRes) == selectedChannelOptionString) if (getString(item.configRes) == selectedChannelOptionString)
return item.modemConfig return item.modemConfig
} }
return ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED return ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED
} }
// Register the launcher and result handler private val requestPermissionAndScanLauncher =
private val barcodeLauncher: ActivityResultLauncher<ScanOptions> = registerForActivityResult( registerForActivityResult(ActivityResultContracts.RequestPermission()) { allowed ->
ScanContract() if (allowed) zxingScan()
) { result: ScanIntentResult -> }
// Register zxing launcher and result handler
private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
if (result.contents != null) { if (result.contents != null) {
((requireActivity() as MainActivity).perhapsChangeChannel(Uri.parse(result.contents))) model.setRequestChannelUrl(Uri.parse(result.contents))
} }
} }
} }