diff options
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt')
-rw-r--r-- | ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt | 61 |
1 files changed, 48 insertions, 13 deletions
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt index 71e409a7..cba7c476 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2021 WireGuard LLC. All Rights Reserved. + * Copyright © 2017-2023 WireGuard LLC. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ package com.wireguard.android.fragment @@ -16,11 +16,14 @@ import android.view.ViewGroup import android.view.animation.Animation import android.view.animation.AnimationUtils import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.activity.addCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar +import com.google.zxing.qrcode.QRCodeReader import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions import com.wireguard.android.Application @@ -30,15 +33,15 @@ import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter.RowC import com.wireguard.android.databinding.TunnelListFragmentBinding import com.wireguard.android.databinding.TunnelListItemBinding import com.wireguard.android.model.ObservableTunnel +import com.wireguard.android.updater.SnackbarUpdateShower import com.wireguard.android.util.ErrorMessages +import com.wireguard.android.util.QrCodeFromFileScanner import com.wireguard.android.util.TunnelImporter import com.wireguard.android.widget.MultiselectableRelativeLayout import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch -import java.util.ArrayList -import java.util.HashSet /** * Fragment containing a list of known WireGuard tunnels. It allows creating and deleting tunnels. @@ -46,13 +49,27 @@ import java.util.HashSet class TunnelListFragment : BaseFragment() { private val actionModeListener = ActionModeListener() private var actionMode: ActionMode? = null + private var backPressedCallback: OnBackPressedCallback? = null private var binding: TunnelListFragmentBinding? = null private val tunnelFileImportResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { data -> if (data == null) return@registerForActivityResult val activity = activity ?: return@registerForActivityResult val contentResolver = activity.contentResolver ?: return@registerForActivityResult activity.lifecycleScope.launch { - TunnelImporter.importTunnel(contentResolver, data) { showSnackbar(it) } + if (QrCodeFromFileScanner.validContentType(contentResolver, data)) { + try { + val qrCodeFromFileScanner = QrCodeFromFileScanner(contentResolver, QRCodeReader()) + val result = qrCodeFromFileScanner.scan(data) + TunnelImporter.importTunnel(parentFragmentManager, result.text) { showSnackbar(it) } + } catch (e: Exception) { + val error = ErrorMessages[e] + val message = Application.get().resources.getString(R.string.import_error, error) + Log.e(TAG, message, e) + showSnackbar(message) + } + } else { + TunnelImporter.importTunnel(contentResolver, data) { showSnackbar(it) } + } } } @@ -64,6 +81,8 @@ class TunnelListFragment : BaseFragment() { } } + private val snackbarUpdateShower = SnackbarUpdateShower(this) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (savedInstanceState != null) { @@ -74,33 +93,45 @@ class TunnelListFragment : BaseFragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { super.onCreateView(inflater, container, savedInstanceState) binding = TunnelListFragmentBinding.inflate(inflater, container, false) val bottomSheet = AddTunnelsSheet() binding?.apply { createFab.setOnClickListener { + if (childFragmentManager.findFragmentByTag("BOTTOM_SHEET") != null) + return@setOnClickListener childFragmentManager.setFragmentResultListener(AddTunnelsSheet.REQUEST_KEY_NEW_TUNNEL, viewLifecycleOwner) { _, bundle -> when (bundle.getString(AddTunnelsSheet.REQUEST_METHOD)) { AddTunnelsSheet.REQUEST_CREATE -> { startActivity(Intent(requireActivity(), TunnelCreatorActivity::class.java)) } + AddTunnelsSheet.REQUEST_IMPORT -> { tunnelFileImportResultLauncher.launch("*/*") } + AddTunnelsSheet.REQUEST_SCAN -> { - qrImportResultLauncher.launch(ScanOptions() + qrImportResultLauncher.launch( + ScanOptions() .setOrientationLocked(false) .setBeepEnabled(false) - .setPrompt(getString(R.string.qr_code_hint))) + .setPrompt(getString(R.string.qr_code_hint)) + ) } } } - bottomSheet.show(childFragmentManager, "BOTTOM_SHEET") + bottomSheet.showNow(childFragmentManager, "BOTTOM_SHEET") } executePendingBindings() + snackbarUpdateShower.attach(mainContainer, createFab) } + backPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { actionMode?.finish() } + backPressedCallback?.isEnabled = false + return binding?.root } @@ -167,8 +198,8 @@ class TunnelListFragment : BaseFragment() { val binding = binding if (binding != null) Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG) - .setAnchorView(binding.createFab) - .show() + .setAnchorView(binding.createFab) + .show() else Toast.makeText(activity ?: Application.get(), message, Toast.LENGTH_SHORT).show() } @@ -210,6 +241,7 @@ class TunnelListFragment : BaseFragment() { mode.finish() true } + R.id.menu_action_select_all -> { lifecycleScope.launch { val tunnels = Application.getTunnelManager().getTunnels() @@ -219,12 +251,14 @@ class TunnelListFragment : BaseFragment() { } true } + else -> false } } override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { actionMode = mode + backPressedCallback?.isEnabled = true if (activity != null) { resources = activity!!.resources } @@ -236,6 +270,7 @@ class TunnelListFragment : BaseFragment() { override fun onDestroyActionMode(mode: ActionMode) { actionMode = null + backPressedCallback?.isEnabled = false resources = null animateFab(binding?.createFab, true) checkedItems.clear() @@ -255,7 +290,7 @@ class TunnelListFragment : BaseFragment() { } val adapter = if (binding == null) null else binding!!.tunnelList.adapter if (actionMode == null && !checkedItems.isEmpty() && activity != null) { - (activity as AppCompatActivity?)!!.startSupportActionMode(this) + (activity as AppCompatActivity).startSupportActionMode(this) } else if (actionMode != null && checkedItems.isEmpty()) { actionMode!!.finish() } @@ -282,7 +317,7 @@ class TunnelListFragment : BaseFragment() { private fun animateFab(view: View?, show: Boolean) { view ?: return val animation = AnimationUtils.loadAnimation( - context, if (show) R.anim.scale_up else R.anim.scale_down + context, if (show) R.anim.scale_up else R.anim.scale_down ) animation.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationRepeat(animation: Animation?) { |