Added QR code scanner as tunnel import method

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Eric Kuck
2018-07-25 02:05:11 +02:00
committed by Jason A. Donenfeld
parent bb20c89cd5
commit 8e0835e570
9 changed files with 231 additions and 21 deletions
@@ -0,0 +1,115 @@
/*
* Copyright © 2018 Eric Kuck <eric@bluelinelabs.com>.
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. 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);
}
}
}
@@ -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) {
@@ -20,6 +20,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -32,6 +33,10 @@ public class Config {
private final Interface interfaceSection = new Interface();
private List<Peer> peers = new ArrayList<>();
public static Config from(final String string) throws IOException {
return from(new BufferedReader(new StringReader(string)));
}
public static Config from(final InputStream stream) throws IOException {
return from(new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)));
}