diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4f56cb1ec..016006d7b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,11 +50,8 @@ - - - + + + + + ) = perms.filter { ) != PackageManager.PERMISSION_GRANTED } +/** + * Camera permission (or empty if we already have what we need) + */ +fun Context.getCameraPermissions(): List { + val perms = mutableListOf(Manifest.permission.CAMERA) + + return getMissingPermissions(perms) +} + +/** @return true if the user already has camera permission */ +fun Context.hasCameraPermission() = getCameraPermissions().isEmpty() + /** * A list of missing background location permissions (or empty if we already have what we need) */ @@ -41,4 +53,4 @@ fun Context.getBackgroundPermissions(): List { } /** @return true if the user already has background location permission */ -fun Context.hasBackgroundPermission() = getBackgroundPermissions().isEmpty() \ No newline at end of file +fun Context.hasBackgroundPermission() = getBackgroundPermissions().isEmpty() diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt index c3a745316..b2ca3fb80 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt @@ -4,6 +4,7 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter +import android.net.Uri import android.os.Bundle import android.os.RemoteException import android.view.LayoutInflater @@ -19,7 +20,9 @@ import com.geeksville.android.Logging import com.geeksville.android.hideKeyboard import com.geeksville.mesh.AppOnlyProtos import com.geeksville.mesh.ChannelProtos +import com.geeksville.mesh.MainActivity import com.geeksville.mesh.R +import com.geeksville.mesh.android.hasCameraPermission import com.geeksville.mesh.databinding.ChannelFragmentBinding import com.geeksville.mesh.model.Channel import com.geeksville.mesh.model.ChannelOption @@ -29,6 +32,8 @@ import com.geeksville.mesh.service.MeshService import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.google.protobuf.ByteString +import com.google.zxing.integration.android.IntentIntegrator +import java.net.MalformedURLException import java.security.SecureRandom @@ -70,6 +75,8 @@ class ChannelFragment : ScreenFragment("Channel"), Logging { binding.channelOptions.isEnabled = isEditing binding.shareButton.isEnabled = !isEditing + binding.resetButton.isEnabled = isEditing + binding.scanButton.isEnabled = isEditing binding.channelNameView.isEnabled = isEditing if (isEditing) // Dim the (stale) QR code while editing... binding.qrView.setDim() @@ -86,7 +93,6 @@ class ChannelFragment : ScreenFragment("Channel"), Logging { // Only let buttons work if we are connected to the radio binding.shareButton.isEnabled = connected - binding.resetButton.isEnabled = connected && Channel.default != channel binding.editableCheckbox.isChecked = false // start locked if (channel != null) { @@ -203,6 +209,28 @@ class ChannelFragment : ScreenFragment("Channel"), Logging { .show() } + binding.scanButton.setOnClickListener { + if ((requireActivity() as MainActivity).hasCameraPermission()) { + val zxingScan = IntentIntegrator.forSupportFragment(this) + zxingScan.setCameraId(0) + zxingScan.setPrompt("") + zxingScan.setBeepEnabled(false) + zxingScan.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE) + zxingScan.initiateScan() + } else { + 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)) { _, _ -> + (requireActivity() as MainActivity).requestCameraPermission() + } + .show() + } + } + // Note: Do not use setOnCheckedChanged here because we don't want to be called when we programmatically disable editing binding.editableCheckbox.setOnClickListener { _ -> @@ -287,4 +315,25 @@ class ChannelFragment : ScreenFragment("Channel"), Logging { return ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) + if (result != null) { + if (result.contents == null) { + Snackbar.make(binding.scanButton, R.string.channel_invalid, Snackbar.LENGTH_LONG).show() + } else { + try { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = ChannelSet(Uri.parse(result.contents)).getChannelUrl(false) + startActivity(intent) + } catch (ex: ActivityNotFoundException) { + Snackbar.make(binding.scanButton, R.string.channel_invalid, Snackbar.LENGTH_LONG).show() + } catch (ex: MalformedURLException) { + Snackbar.make(binding.scanButton, R.string.channel_invalid, Snackbar.LENGTH_LONG).show() + } + } + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } } diff --git a/app/src/main/res/layout/channel_fragment.xml b/app/src/main/res/layout/channel_fragment.xml index af5ea0dbb..d916757ca 100644 --- a/app/src/main/res/layout/channel_fragment.xml +++ b/app/src/main/res/layout/channel_fragment.xml @@ -98,30 +98,40 @@ android:id="@+id/resetButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="32dp" + android:layout_marginEnd="10dp" android:text="@string/reset" app:icon="@drawable/ic_twotone_public_24" app:layout_constraintBottom_toBottomOf="@id/bottomButtonsGuideline" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintEnd_toStartOf="@id/editableCheckbox" /> + +