From 8e0835e57084be1216e3ef1e71941295b2df909d Mon Sep 17 00:00:00 2001 From: Eric Kuck Date: Tue, 24 Jul 2018 12:32:40 -0500 Subject: Added QR code scanner as tunnel import method Signed-off-by: Jason A. Donenfeld --- .../fragment/ConfigNamingDialogFragment.java | 115 +++++++++++++++++++++ .../android/fragment/TunnelListFragment.java | 35 +++++++ 2 files changed, 150 insertions(+) create mode 100644 app/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.java (limited to 'app/src/main/java/com/wireguard/android/fragment') diff --git a/app/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.java b/app/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.java new file mode 100644 index 00000000..1a3ec0d2 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.java @@ -0,0 +1,115 @@ +/* + * Copyright © 2018 Eric Kuck . + * Copyright © 2018 Jason A. Donenfeld . All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.wireguard.android.fragment; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +import com.wireguard.android.Application; +import com.wireguard.android.R; +import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding; +import com.wireguard.config.Config; + +import java.io.IOException; +import java.util.Objects; + +public class ConfigNamingDialogFragment extends DialogFragment { + + private static final String KEY_CONFIG_TEXT = "config_text"; + + @Nullable private Config config; + @Nullable private ConfigNamingDialogFragmentBinding binding; + @Nullable private InputMethodManager imm; + + public static ConfigNamingDialogFragment newInstance(final String configText) { + final Bundle extras = new Bundle(); + extras.putString(KEY_CONFIG_TEXT, configText); + final ConfigNamingDialogFragment fragment = new ConfigNamingDialogFragment(); + fragment.setArguments(extras); + return fragment; + } + + @Override + public void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + config = Config.from(getArguments().getString(KEY_CONFIG_TEXT)); + } catch (final IOException exception) { + throw new RuntimeException("Invalid config passed to " + getClass().getSimpleName(), exception); + } + } + + @Override public void onResume() { + super.onResume(); + + final AlertDialog dialog = (AlertDialog) getDialog(); + if (dialog != null) { + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> createTunnelAndDismiss()); + + setKeyboardVisible(true); + } + } + + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + final Activity activity = getActivity(); + + imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + + final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); + alertDialogBuilder.setTitle(R.string.create_tunnel); + + binding = ConfigNamingDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false); + binding.executePendingBindings(); + alertDialogBuilder.setView(binding.getRoot()); + + alertDialogBuilder.setPositiveButton(R.string.create_tunnel, null); + alertDialogBuilder.setNegativeButton(R.string.cancel, (dialog, which) -> dismiss()); + + return alertDialogBuilder.create(); + } + + @Override + public void dismiss() { + setKeyboardVisible(false); + super.dismiss(); + } + + private void createTunnelAndDismiss() { + if (binding != null) { + final String name = binding.tunnelNameText.getText().toString(); + + Application.getTunnelManager().create(name, config).whenComplete((tunnel, throwable) -> { + if (tunnel != null) { + dismiss(); + } else { + binding.tunnelNameTextLayout.setError(throwable.getMessage()); + } + }); + } + } + + private void setKeyboardVisible(final boolean visible) { + Objects.requireNonNull(imm); + + if (visible) { + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } else if (binding != null) { + imm.hideSoftInputFromWindow(binding.tunnelNameText.getWindowToken(), 0); + } + } + +} diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java index d0019c91..23e449b6 100644 --- a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java +++ b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java @@ -18,6 +18,7 @@ import android.provider.OpenableColumns; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; +import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.util.Log; @@ -27,6 +28,8 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; import com.wireguard.android.Application; import com.wireguard.android.R; import com.wireguard.android.activity.TunnelCreatorActivity; @@ -39,6 +42,7 @@ import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollLis import com.wireguard.config.Config; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -72,6 +76,22 @@ public class TunnelListFragment extends BaseFragment { return false; } + private void importTunnel(@NonNull final String configText) { + try { + // Ensure the config text is parseable before proceeding… + Config.from(configText); + + // Config text is valid, now create the tunnel… + final FragmentManager fragmentManager = getFragmentManager(); + if (fragmentManager != null) { + final ConfigNamingDialogFragment fragment = ConfigNamingDialogFragment.newInstance(configText); + fragment.show(fragmentManager, null); + } + } catch (final IllegalArgumentException|IOException exception) { + onTunnelImportFinished(Collections.emptyList(), Collections.singletonList(exception)); + } + } + private void importTunnel(@Nullable final Uri uri) { final Activity activity = getActivity(); if (activity == null || uri == null) @@ -172,6 +192,12 @@ public class TunnelListFragment extends BaseFragment { if (resultCode == Activity.RESULT_OK && data != null) importTunnel(data.getData()); return; + case IntentIntegrator.REQUEST_CODE: + final IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); + if (result != null && result.getContents() != null) { + importTunnel(result.getContents()); + } + return; default: super.onActivityResult(requestCode, resultCode, data); } @@ -217,6 +243,15 @@ public class TunnelListFragment extends BaseFragment { binding.createMenu.collapse(); } + public void onRequestScanQRCode(@SuppressWarnings("unused") final View view) { + final IntentIntegrator intentIntegrator = IntentIntegrator.forSupportFragment(this); + intentIntegrator.setOrientationLocked(false); + intentIntegrator.initiateScan(Collections.singletonList(IntentIntegrator.QR_CODE)); + + if (binding != null) + binding.createMenu.collapse(); + } + @Override public void onPause() { if (binding != null) { -- cgit v1.2.3