Request VPN permissions on activation
Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
parent
d50e0f5fb9
commit
d7ea078cdf
@ -25,7 +25,7 @@ import com.wireguard.android.util.ToolsInstaller;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class Application extends android.app.Application {
|
public class Application extends android.app.Application {
|
||||||
@ -37,7 +37,7 @@ public class Application extends android.app.Application {
|
|||||||
private ToolsInstaller toolsInstaller;
|
private ToolsInstaller toolsInstaller;
|
||||||
private TunnelManager tunnelManager;
|
private TunnelManager tunnelManager;
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private List<BackendCallback> haveBackendCallbacks = new ArrayList<>();
|
private Collection<BackendCallback> haveBackendCallbacks = new ArrayList<>();
|
||||||
private final Object haveBackendCallbacksLock = new Object();
|
private final Object haveBackendCallbacksLock = new Object();
|
||||||
|
|
||||||
public Application() {
|
public Application() {
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
|
|
||||||
package com.wireguard.android.activity;
|
package com.wireguard.android.activity;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.databinding.CallbackRegistry;
|
import android.databinding.CallbackRegistry;
|
||||||
import android.databinding.CallbackRegistry.NotifierCallback;
|
import android.databinding.CallbackRegistry.NotifierCallback;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.wireguard.android.Application;
|
import com.wireguard.android.Application;
|
||||||
import com.wireguard.android.backend.GoBackend;
|
|
||||||
import com.wireguard.android.model.Tunnel;
|
import com.wireguard.android.model.Tunnel;
|
||||||
import com.wireguard.android.model.TunnelManager;
|
import com.wireguard.android.model.TunnelManager;
|
||||||
|
|
||||||
@ -52,14 +50,6 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
|
|||||||
|
|
||||||
// The selected tunnel must be set before the superclass method recreates fragments.
|
// The selected tunnel must be set before the superclass method recreates fragments.
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Application.onHaveBackend(backend -> {
|
|
||||||
if (backend instanceof GoBackend) {
|
|
||||||
final Intent intent = GoBackend.VpnService.prepare(this);
|
|
||||||
if (intent != null)
|
|
||||||
startActivityForResult(intent, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,7 +14,6 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.RecyclerView.Adapter;
|
import android.support.v7.widget.RecyclerView.Adapter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.wireguard.android.BR;
|
import com.wireguard.android.BR;
|
||||||
@ -78,7 +77,7 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
|
|||||||
holder.binding.executePendingBindings();
|
holder.binding.executePendingBindings();
|
||||||
|
|
||||||
if (rowConfigurationHandler != null) {
|
if (rowConfigurationHandler != null) {
|
||||||
rowConfigurationHandler.onConfigureRow(holder.binding.getRoot(), getItem(position), position);
|
rowConfigurationHandler.onConfigureRow(holder.binding, getItem(position), position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,8 +148,8 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface RowConfigurationHandler<T> {
|
public interface RowConfigurationHandler<B extends ViewDataBinding, T> {
|
||||||
void onConfigureRow(View view, T item, int position);
|
void onConfigureRow(B binding, T item, int position);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,25 @@
|
|||||||
package com.wireguard.android.fragment;
|
package com.wireguard.android.fragment;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.databinding.ViewDataBinding;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.wireguard.android.Application;
|
||||||
|
import com.wireguard.android.R;
|
||||||
import com.wireguard.android.activity.BaseActivity;
|
import com.wireguard.android.activity.BaseActivity;
|
||||||
import com.wireguard.android.activity.BaseActivity.OnSelectedTunnelChangedListener;
|
import com.wireguard.android.activity.BaseActivity.OnSelectedTunnelChangedListener;
|
||||||
|
import com.wireguard.android.backend.GoBackend;
|
||||||
|
import com.wireguard.android.databinding.TunnelDetailFragmentBinding;
|
||||||
|
import com.wireguard.android.databinding.TunnelListItemBinding;
|
||||||
import com.wireguard.android.model.Tunnel;
|
import com.wireguard.android.model.Tunnel;
|
||||||
|
import com.wireguard.android.model.Tunnel.State;
|
||||||
|
import com.wireguard.android.util.ExceptionLoggers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for fragments that need to know the currently-selected tunnel. Only does anything when
|
* Base class for fragments that need to know the currently-selected tunnel. Only does anything when
|
||||||
@ -19,7 +33,12 @@ import com.wireguard.android.model.Tunnel;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class BaseFragment extends Fragment implements OnSelectedTunnelChangedListener {
|
public abstract class BaseFragment extends Fragment implements OnSelectedTunnelChangedListener {
|
||||||
|
private static final String TAG = "WireGuard/" + BaseFragment.class.getSimpleName();
|
||||||
|
private static final int REQUEST_CODE_VPN_PERMISSION = 23491;
|
||||||
|
|
||||||
private BaseActivity activity;
|
private BaseActivity activity;
|
||||||
|
private Tunnel pendingTunnel;
|
||||||
|
private Boolean pendingTunnelUp;
|
||||||
|
|
||||||
protected Tunnel getSelectedTunnel() {
|
protected Tunnel getSelectedTunnel() {
|
||||||
return activity != null ? activity.getSelectedTunnel() : null;
|
return activity != null ? activity.getSelectedTunnel() : null;
|
||||||
@ -44,8 +63,64 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
|
|||||||
super.onDetach();
|
super.onDetach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (requestCode == REQUEST_CODE_VPN_PERMISSION) {
|
||||||
|
if (pendingTunnel != null && pendingTunnelUp != null)
|
||||||
|
setTunnelStateWithPermissionsResult(pendingTunnel, pendingTunnelUp);
|
||||||
|
pendingTunnel = null;
|
||||||
|
pendingTunnelUp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void setSelectedTunnel(final Tunnel tunnel) {
|
protected void setSelectedTunnel(final Tunnel tunnel) {
|
||||||
if (activity != null)
|
if (activity != null)
|
||||||
activity.setSelectedTunnel(tunnel);
|
activity.setSelectedTunnel(tunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTunnelState(final View view, final boolean checked) {
|
||||||
|
final ViewDataBinding binding = DataBindingUtil.findBinding(view);
|
||||||
|
final Tunnel tunnel;
|
||||||
|
if (binding instanceof TunnelDetailFragmentBinding)
|
||||||
|
tunnel = ((TunnelDetailFragmentBinding) binding).getTunnel();
|
||||||
|
else if (binding instanceof TunnelListItemBinding)
|
||||||
|
tunnel = ((TunnelListItemBinding) binding).getItem();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
Application.onHaveBackend(backend -> {
|
||||||
|
if (backend instanceof GoBackend) {
|
||||||
|
final Intent intent = GoBackend.VpnService.prepare(view.getContext());
|
||||||
|
if (intent != null) {
|
||||||
|
pendingTunnel = tunnel;
|
||||||
|
pendingTunnelUp = checked;
|
||||||
|
startActivityForResult(intent, REQUEST_CODE_VPN_PERMISSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTunnelStateWithPermissionsResult(tunnel, checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTunnelStateWithPermissionsResult(@NonNull final Tunnel tunnel, final boolean checked) {
|
||||||
|
tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> {
|
||||||
|
if (throwable == null)
|
||||||
|
return;
|
||||||
|
final View view = getView();
|
||||||
|
if (view == null) {
|
||||||
|
Log.e(TAG, "setTunnelStateWithPermissionsResult() with no view");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Context context = view.getContext();
|
||||||
|
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||||
|
final int messageResId = checked ? R.string.error_up : R.string.error_down;
|
||||||
|
final String message = context.getString(messageResId, error);
|
||||||
|
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
|
||||||
|
Log.e(TAG, message, throwable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
|
||||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wireguard.android.fragment;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.databinding.DataBindingUtil;
|
|
||||||
import android.databinding.ViewDataBinding;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.wireguard.android.R;
|
|
||||||
import com.wireguard.android.databinding.TunnelDetailFragmentBinding;
|
|
||||||
import com.wireguard.android.databinding.TunnelListItemBinding;
|
|
||||||
import com.wireguard.android.model.Tunnel;
|
|
||||||
import com.wireguard.android.model.Tunnel.State;
|
|
||||||
import com.wireguard.android.util.ExceptionLoggers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method shared by TunnelListFragment and TunnelDetailFragment.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class TunnelController {
|
|
||||||
private static final String TAG = "WireGuard/" + TunnelController.class.getSimpleName();
|
|
||||||
|
|
||||||
private TunnelController() {
|
|
||||||
// Prevent instantiation.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTunnelState(final View view, final boolean checked) {
|
|
||||||
final ViewDataBinding binding = DataBindingUtil.findBinding(view);
|
|
||||||
final Tunnel tunnel;
|
|
||||||
if (binding instanceof TunnelDetailFragmentBinding)
|
|
||||||
tunnel = ((TunnelDetailFragmentBinding) binding).getTunnel();
|
|
||||||
else if (binding instanceof TunnelListItemBinding)
|
|
||||||
tunnel = ((TunnelListItemBinding) binding).getItem();
|
|
||||||
else
|
|
||||||
tunnel = null;
|
|
||||||
if (tunnel == null) {
|
|
||||||
Log.e(TAG, "setChecked() from a null tunnel", new IllegalStateException("No tunnel"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> {
|
|
||||||
if (throwable == null)
|
|
||||||
return;
|
|
||||||
final Context context = view.getContext();
|
|
||||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
|
||||||
final int messageResId = checked ? R.string.error_up : R.string.error_down;
|
|
||||||
final String message = context.getString(messageResId, error);
|
|
||||||
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
|
|
||||||
Log.e(TAG, message, throwable);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -69,7 +69,9 @@ public class TunnelDetailFragment extends BaseFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewStateRestored(final Bundle savedInstanceState) {
|
public void onViewStateRestored(final Bundle savedInstanceState) {
|
||||||
|
binding.setFragment(this);
|
||||||
onSelectedTunnelChanged(null, getSelectedTunnel());
|
onSelectedTunnelChanged(null, getSelectedTunnel());
|
||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,10 @@ import com.wireguard.android.R;
|
|||||||
import com.wireguard.android.activity.TunnelCreatorActivity;
|
import com.wireguard.android.activity.TunnelCreatorActivity;
|
||||||
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter;
|
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter;
|
||||||
import com.wireguard.android.databinding.TunnelListFragmentBinding;
|
import com.wireguard.android.databinding.TunnelListFragmentBinding;
|
||||||
|
import com.wireguard.android.databinding.TunnelListItemBinding;
|
||||||
import com.wireguard.android.model.Tunnel;
|
import com.wireguard.android.model.Tunnel;
|
||||||
import com.wireguard.android.util.ExceptionLoggers;
|
import com.wireguard.android.util.ExceptionLoggers;
|
||||||
|
import com.wireguard.android.widget.ToggleSwitch;
|
||||||
import com.wireguard.config.Config;
|
import com.wireguard.config.Config;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@ -269,23 +271,21 @@ public class TunnelListFragment extends BaseFragment {
|
|||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
binding.setFragment(this);
|
binding.setFragment(this);
|
||||||
binding.setTunnels(Application.getTunnelManager().getTunnels());
|
binding.setTunnels(Application.getTunnelManager().getTunnels());
|
||||||
binding.setRowConfigurationHandler(new ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<Tunnel>() {
|
binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> {
|
||||||
@Override
|
binding.setFragment(this);
|
||||||
public void onConfigureRow(View view, Tunnel tunnel, int position) {
|
binding.getRoot().setOnClickListener(clicked -> {
|
||||||
view.setOnClickListener(clicked -> {
|
if (actionMode == null) {
|
||||||
if (actionMode == null) {
|
setSelectedTunnel(tunnel);
|
||||||
setSelectedTunnel(tunnel);
|
} else {
|
||||||
} else {
|
|
||||||
actionModeListener.toggleItemChecked(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
view.setOnLongClickListener(clicked -> {
|
|
||||||
actionModeListener.toggleItemChecked(position);
|
actionModeListener.toggleItemChecked(position);
|
||||||
return true;
|
}
|
||||||
});
|
});
|
||||||
|
binding.getRoot().setOnLongClickListener(clicked -> {
|
||||||
|
actionModeListener.toggleItemChecked(position);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
view.setActivated(actionModeListener.checkedItems.contains(position));
|
binding.getRoot().setActivated(actionModeListener.checkedItems.contains(position));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<import type="com.wireguard.android.fragment.TunnelController" />
|
|
||||||
|
|
||||||
<import type="com.wireguard.android.model.Tunnel.State" />
|
<import type="com.wireguard.android.model.Tunnel.State" />
|
||||||
|
|
||||||
<import type="com.wireguard.android.util.ClipboardUtils" />
|
<import type="com.wireguard.android.util.ClipboardUtils" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="fragment"
|
||||||
|
type="com.wireguard.android.fragment.TunnelDetailFragment" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="tunnel"
|
name="tunnel"
|
||||||
type="com.wireguard.android.model.Tunnel" />
|
type="com.wireguard.android.model.Tunnel" />
|
||||||
@ -63,7 +65,7 @@
|
|||||||
android:layout_alignBaseline="@+id/interface_title"
|
android:layout_alignBaseline="@+id/interface_title"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
app:checked="@{tunnel.state == State.UP}"
|
app:checked="@{tunnel.state == State.UP}"
|
||||||
app:onBeforeCheckedChanged="@{TunnelController::setTunnelState}" />
|
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/interface_name_label"
|
android:id="@+id/interface_name_label"
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<import type="com.wireguard.android.fragment.TunnelController" />
|
|
||||||
|
|
||||||
<import type="com.wireguard.android.model.Tunnel" />
|
<import type="com.wireguard.android.model.Tunnel" />
|
||||||
|
|
||||||
<import type="com.wireguard.android.model.Tunnel.State" />
|
<import type="com.wireguard.android.model.Tunnel.State" />
|
||||||
@ -21,6 +19,10 @@
|
|||||||
<variable
|
<variable
|
||||||
name="item"
|
name="item"
|
||||||
type="com.wireguard.android.model.Tunnel" />
|
type="com.wireguard.android.model.Tunnel" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="fragment"
|
||||||
|
type="com.wireguard.android.fragment.TunnelListFragment" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
@ -49,6 +51,6 @@
|
|||||||
android:layout_alignBaseline="@+id/tunnel_name"
|
android:layout_alignBaseline="@+id/tunnel_name"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
app:checked="@{item.state == State.UP}"
|
app:checked="@{item.state == State.UP}"
|
||||||
app:onBeforeCheckedChanged="@{TunnelController::setTunnelState}" />
|
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</layout>
|
</layout>
|
||||||
|
Loading…
Reference in New Issue
Block a user