mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c905ef6083 | |||
| 9652fe99df | |||
| 62d8beff96 | |||
| 7d438e9dbc | |||
| b364221c93 | |||
| b8052bd8fb | |||
| e437ac389e | |||
| 2db2a0921d | |||
| d7889c4e88 | |||
| 559add3f71 | |||
| d615304e83 | |||
| 8e0835e570 | |||
| bb20c89cd5 | |||
| 996cbb5f2b | |||
| 8028d708cb | |||
| 7689905c78 | |||
| 284e42647c | |||
| 3a16c08821 | |||
| 76fb6a318e | |||
| c633f96374 | |||
| 3357be9557 | |||
| 6696e838da | |||
| d0d56f3a1b |
+17
-2
@@ -17,8 +17,8 @@ android {
|
||||
applicationId 'com.wireguard.android'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 27
|
||||
versionCode 435
|
||||
versionName '0.0.20180720'
|
||||
versionCode 437
|
||||
versionName '0.0.20180725'
|
||||
}
|
||||
// If the keystore file exists
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
@@ -40,10 +40,20 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
if (keystorePropertiesFile.exists()) signingConfig signingConfigs.release
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${android.defaultConfig.applicationId}"
|
||||
}
|
||||
}
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
versionNameSuffix "-debug"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${android.defaultConfig.applicationId}${applicationIdSuffix}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
@@ -58,6 +68,8 @@ ext {
|
||||
supportLibsVersion = '27.1.1'
|
||||
streamsupportVersion = '1.6.0'
|
||||
jsr305Version = '3.0.2'
|
||||
zxingEmbeddedVersion = '3.6.0'
|
||||
acraVersion = '5.2.0-rc2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -70,6 +82,9 @@ dependencies {
|
||||
implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion"
|
||||
implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion"
|
||||
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
|
||||
implementation "com.journeyapps:zxing-android-embedded:$zxingEmbeddedVersion"
|
||||
implementation "ch.acra:acra-http:$acraVersion"
|
||||
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package="com.wireguard.android"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
@@ -50,6 +51,11 @@
|
||||
android:label="@string/create_activity_title"
|
||||
android:parentActivityName=".activity.MainActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="fullSensor"
|
||||
tools:replace="screenOrientation" />
|
||||
|
||||
<receiver android:name=".BootShutdownReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
package com.wireguard.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -16,20 +19,36 @@ import android.support.v7.app.AppCompatDelegate;
|
||||
import com.wireguard.android.backend.Backend;
|
||||
import com.wireguard.android.backend.GoBackend;
|
||||
import com.wireguard.android.backend.WgQuickBackend;
|
||||
import com.wireguard.android.configStore.ConfigStore;
|
||||
import com.wireguard.android.configStore.FileConfigStore;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
import com.wireguard.android.util.AsyncWorker;
|
||||
import com.wireguard.android.util.RootShell;
|
||||
import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.annotation.AcraCore;
|
||||
import org.acra.annotation.AcraHttpSender;
|
||||
import org.acra.data.StringFormat;
|
||||
import org.acra.sender.HttpSender;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
|
||||
|
||||
@AcraCore(reportFormat = StringFormat.JSON,
|
||||
buildConfigClass = BuildConfig.class,
|
||||
logcatArguments = {"-b", "all", "-d", "-v", "threadtime", "*:V"},
|
||||
excludeMatchingSharedPreferencesKeys = {"last_used_tunnel", "enabled_configs"})
|
||||
@AcraHttpSender(uri = "https://crashreport.zx2c4.com/android/report",
|
||||
basicAuthLogin = "6RCovLxEVCTXGiW5",
|
||||
basicAuthPassword = "O7I3sVa5ULVdiC51",
|
||||
httpMethod = HttpSender.Method.POST,
|
||||
compress = true)
|
||||
public class Application extends android.app.Application {
|
||||
@SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
|
||||
@SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
|
||||
@@ -37,15 +56,49 @@ public class Application extends android.app.Application {
|
||||
@SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
|
||||
@SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
|
||||
@SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
|
||||
@SuppressWarnings("NullableProblems") private Handler handler;
|
||||
@Nullable private Backend backend;
|
||||
@Nullable private Collection<BackendCallback> haveBackendCallbacks = new ArrayList<>();
|
||||
private final Object haveBackendCallbacksLock = new Object();
|
||||
private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
|
||||
|
||||
public Application() {
|
||||
weakSelf = new WeakReference<>(this);
|
||||
}
|
||||
|
||||
/* The ACRA password can be trivially reverse engineered and is open source anyway,
|
||||
* so there's no point in trying to protect it. However, we do want to at least
|
||||
* prevent innocent self-builders from uploading stuff to our crash reporter. So, we
|
||||
* check the DN of the certs that signed the apk, without even bothering to try
|
||||
* validating that they're authentic. It's a good enough heuristic.
|
||||
*/
|
||||
private static boolean shouldEnableCrashReporting(final Context context) {
|
||||
if (BuildConfig.DEBUG)
|
||||
return false;
|
||||
try {
|
||||
final CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||||
for (final Signature sig : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures) {
|
||||
try {
|
||||
for (final String category : ((X509Certificate) cf.generateCertificate(new ByteArrayInputStream(sig.toByteArray()))).getSubjectDN().getName().split(", *")) {
|
||||
final String[] parts = category.split("=", 2);
|
||||
if (!"O".equals(parts[0]))
|
||||
continue;
|
||||
switch (parts[1]) {
|
||||
case "Google Inc.":
|
||||
case "fdroid.org":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (final Exception ignored) { }
|
||||
}
|
||||
} catch (final Exception ignored) { }
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(final Context context) {
|
||||
super.attachBaseContext(context);
|
||||
if (shouldEnableCrashReporting(context))
|
||||
ACRA.init(this);
|
||||
}
|
||||
|
||||
public static Application get() {
|
||||
return weakSelf.get();
|
||||
}
|
||||
@@ -56,43 +109,26 @@ public class Application extends android.app.Application {
|
||||
|
||||
public static Backend getBackend() {
|
||||
final Application app = get();
|
||||
synchronized (app) {
|
||||
synchronized (app.futureBackend) {
|
||||
if (app.backend == null) {
|
||||
Backend backend = null;
|
||||
if (new File("/sys/module/wireguard").exists()) {
|
||||
try {
|
||||
app.rootShell.start();
|
||||
app.backend = new WgQuickBackend(app.getApplicationContext());
|
||||
} catch (final Exception ignored) { }
|
||||
}
|
||||
if (app.backend == null)
|
||||
app.backend = new GoBackend(app.getApplicationContext());
|
||||
synchronized (app.haveBackendCallbacksLock) {
|
||||
if (app.haveBackendCallbacks != null) {
|
||||
for (final BackendCallback callback : app.haveBackendCallbacks)
|
||||
app.handler.post(() -> callback.callback(app.backend));
|
||||
app.haveBackendCallbacks = null;
|
||||
backend = new WgQuickBackend(app.getApplicationContext());
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (backend == null)
|
||||
backend = new GoBackend(app.getApplicationContext());
|
||||
app.backend = backend;
|
||||
}
|
||||
return app.backend;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BackendCallback {
|
||||
void callback(final Backend backend);
|
||||
}
|
||||
|
||||
public static void onHaveBackend(final BackendCallback callback) {
|
||||
final Application app = get();
|
||||
synchronized (app.haveBackendCallbacksLock) {
|
||||
if (app.haveBackendCallbacks == null) {
|
||||
Objects.requireNonNull(app.backend, "Backend still null in onHaveBackend call");
|
||||
callback.callback(app.backend);
|
||||
} else {
|
||||
app.haveBackendCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
public static CompletableFuture<Backend> getBackendAsync() {
|
||||
return get().futureBackend;
|
||||
}
|
||||
|
||||
public static RootShell getRootShell() {
|
||||
@@ -115,11 +151,7 @@ public class Application extends android.app.Application {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
final Executor executor = AsyncTask.SERIAL_EXECUTOR;
|
||||
final ConfigStore configStore = new FileConfigStore(getApplicationContext());
|
||||
|
||||
asyncWorker = new AsyncWorker(executor, handler);
|
||||
asyncWorker = new AsyncWorker(AsyncTask.SERIAL_EXECUTOR, new Handler(Looper.getMainLooper()));
|
||||
rootShell = new RootShell(getApplicationContext());
|
||||
toolsInstaller = new ToolsInstaller(getApplicationContext());
|
||||
|
||||
@@ -128,8 +160,14 @@ public class Application extends android.app.Application {
|
||||
sharedPreferences.getBoolean("dark_theme", false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
|
||||
|
||||
tunnelManager = new TunnelManager(configStore);
|
||||
asyncWorker.runAsync(Application::getBackend);
|
||||
tunnelManager = new TunnelManager(new FileConfigStore(getApplicationContext()));
|
||||
tunnelManager.onCreate();
|
||||
|
||||
asyncWorker.supplyAsync(Application::getBackend).thenAccept(backend -> {
|
||||
futureBackend.complete(backend);
|
||||
ACRA.getErrorReporter().putCustomData("backend", backend.getClass().getSimpleName());
|
||||
asyncWorker.supplyAsync(backend::getVersion).thenAccept(version ->
|
||||
ACRA.getErrorReporter().putCustomData("backendVersion", version));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class BootShutdownReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
Application.onHaveBackend(backend -> {
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
if (!(backend instanceof WgQuickBackend))
|
||||
return;
|
||||
final String action = intent.getAction();
|
||||
|
||||
@@ -13,7 +13,6 @@ import android.support.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -39,15 +38,17 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
|
||||
String savedTunnelName = null;
|
||||
final String savedTunnelName;
|
||||
if (savedInstanceState != null)
|
||||
savedTunnelName = savedInstanceState.getString(KEY_SELECTED_TUNNEL);
|
||||
else if (getIntent() != null)
|
||||
savedTunnelName = getIntent().getStringExtra(KEY_SELECTED_TUNNEL);
|
||||
if (savedTunnelName != null) {
|
||||
final TunnelManager tunnelManager = Application.getTunnelManager();
|
||||
selectedTunnel = tunnelManager.getTunnels().get(savedTunnelName);
|
||||
}
|
||||
else
|
||||
savedTunnelName = null;
|
||||
|
||||
if (savedTunnelName != null)
|
||||
Application.getTunnelManager().getTunnels()
|
||||
.thenAccept(tunnels -> setSelectedTunnel(tunnels.get(savedTunnelName)));
|
||||
|
||||
// The selected tunnel must be set before the superclass method recreates fragments.
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MainActivity extends BaseActivity {
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
if (state.layer > 0)
|
||||
transaction.addToBackStack(null);
|
||||
transaction.commit();
|
||||
transaction.commitAllowingStateLoss(); /* TODO: switch back to .commit() when this function is rewritten. */
|
||||
} else if (nextState.layer == state.layer - 1) {
|
||||
if (fragmentManager.getBackStackEntryCount() == 0)
|
||||
return false;
|
||||
|
||||
@@ -99,7 +99,7 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||
for (final Preference pref : wgQuickOnlyPrefs)
|
||||
pref.setVisible(false);
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
Application.onHaveBackend(backend -> {
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
for (final Preference pref : wgQuickOnlyPrefs) {
|
||||
if (backend instanceof WgQuickBackend)
|
||||
pref.setVisible(true);
|
||||
|
||||
@@ -18,6 +18,37 @@ import com.wireguard.android.Application;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public abstract class ThemeChangeAwareActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final String TAG = "WireGuard/" + ThemeChangeAwareActivity.class.getSimpleName();
|
||||
|
||||
@Nullable private static Resources lastResources;
|
||||
private static boolean lastDarkMode;
|
||||
private static synchronized void invalidateDrawableCache(final Resources resources, final boolean darkMode) {
|
||||
if (resources == lastResources && darkMode == lastDarkMode)
|
||||
return;
|
||||
|
||||
try {
|
||||
Field f;
|
||||
Object o = resources;
|
||||
try {
|
||||
f = o.getClass().getDeclaredField("mResourcesImpl");
|
||||
f.setAccessible(true);
|
||||
o = f.get(o);
|
||||
} catch (final Exception ignored) { }
|
||||
f = o.getClass().getDeclaredField("mDrawableCache");
|
||||
f.setAccessible(true);
|
||||
o = f.get(o);
|
||||
try {
|
||||
o.getClass().getMethod("onConfigurationChange", int.class).invoke(o, -1);
|
||||
} catch (final Exception ignored) {
|
||||
o.getClass().getMethod("clear").invoke(o);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Failed to flush drawable cache", e);
|
||||
}
|
||||
|
||||
lastResources = resources;
|
||||
lastDarkMode = darkMode;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -35,10 +66,12 @@ public abstract class ThemeChangeAwareActivity extends AppCompatActivity impleme
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
|
||||
if ("dark_theme".equals(key)) {
|
||||
final boolean darkMode = sharedPreferences.getBoolean(key, false);
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
sharedPreferences.getBoolean(key, false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES :
|
||||
AppCompatDelegate.MODE_NIGHT_NO);
|
||||
invalidateDrawableCache(getResources(), darkMode);
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,10 +240,13 @@ public final class GoBackend implements Backend {
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
for (final Tunnel tunnel : Application.getTunnelManager().getTunnels()) {
|
||||
if (tunnel != null && tunnel.getState() != State.DOWN)
|
||||
tunnel.setState(State.DOWN);
|
||||
}
|
||||
Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
|
||||
for (final Tunnel tunnel : tunnels) {
|
||||
if (tunnel != null && tunnel.getState() != State.DOWN)
|
||||
tunnel.setState(State.DOWN);
|
||||
}
|
||||
});
|
||||
|
||||
vpnService = vpnService.newIncompleteFuture();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package com.wireguard.android.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -22,10 +21,8 @@ import android.widget.Toast;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.activity.BaseActivity;
|
||||
import com.wireguard.android.databinding.AppListDialogFragmentBinding;
|
||||
import com.wireguard.android.model.ApplicationData;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ObservableKeyedArrayList;
|
||||
import com.wireguard.android.util.ObservableKeyedList;
|
||||
|
||||
@@ -94,7 +94,7 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
|
||||
if (tunnel == null)
|
||||
return;
|
||||
|
||||
Application.onHaveBackend(backend -> {
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
if (backend instanceof GoBackend) {
|
||||
final Intent intent = GoBackend.VpnService.prepare(view.getContext());
|
||||
if (intent != null) {
|
||||
|
||||
@@ -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.import_from_qrcode);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,8 @@ import com.wireguard.config.Attribute;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.Peer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -106,6 +108,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
}
|
||||
};
|
||||
|
||||
private final Collection<Object> breakObjectOrientedLayeringHandlerReceivers = new ArrayList<>();
|
||||
private final Observable.OnPropertyChangedCallback breakObjectOrientedLayeringHandler = new Observable.OnPropertyChangedCallback() {
|
||||
@Override
|
||||
public void onPropertyChanged(final Observable sender, final int propertyId) {
|
||||
@@ -116,8 +119,11 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
return;
|
||||
if (propertyId == BR.config) {
|
||||
config.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
|
||||
breakObjectOrientedLayeringHandlerReceivers.add(config);
|
||||
config.getInterfaceSection().addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
|
||||
breakObjectOrientedLayeringHandlerReceivers.add(config.getInterfaceSection());
|
||||
config.getPeers().addOnListChangedCallback(breakObjectListOrientedLayeringHandler);
|
||||
breakObjectOrientedLayeringHandlerReceivers.add(config.getPeers());
|
||||
} else if (propertyId == BR.dnses || propertyId == BR.peers)
|
||||
;
|
||||
else
|
||||
@@ -136,13 +142,21 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
binding = TunnelEditorFragmentBinding.inflate(inflater, container, false);
|
||||
binding.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
|
||||
breakObjectOrientedLayeringHandlerReceivers.add(binding);
|
||||
binding.executePendingBindings();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
binding = null;
|
||||
for (final Object o : breakObjectOrientedLayeringHandlerReceivers) {
|
||||
if (o instanceof Observable)
|
||||
((Observable)o).removeOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
|
||||
else if (o instanceof ObservableList)
|
||||
((ObservableList)o).removeOnListChangedCallback(breakObjectListOrientedLayeringHandler);
|
||||
}
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
package com.wireguard.android.fragment;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
@@ -19,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,9 +27,9 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
|
||||
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;
|
||||
@@ -38,10 +38,11 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding;
|
||||
import com.wireguard.android.databinding.TunnelListItemBinding;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.widget.CustomRecyclerViewScrollListener;
|
||||
import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollListener;
|
||||
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;
|
||||
@@ -62,8 +63,6 @@ import java9.util.stream.StreamSupport;
|
||||
public class TunnelListFragment extends BaseFragment {
|
||||
private static final int REQUEST_IMPORT = 1;
|
||||
private static final String TAG = "WireGuard/" + TunnelListFragment.class.getSimpleName();
|
||||
private static final TimeInterpolator FAB_SHOW_ANIMATION = new DecelerateInterpolator(2);
|
||||
private static final TimeInterpolator FAB_HIDE_ANIMATION = new AccelerateInterpolator(2);
|
||||
|
||||
private final ActionModeListener actionModeListener = new ActionModeListener();
|
||||
@Nullable private ActionMode actionMode;
|
||||
@@ -77,6 +76,20 @@ 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)
|
||||
ConfigNamingDialogFragment.newInstance(configText).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)
|
||||
@@ -177,6 +190,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);
|
||||
}
|
||||
@@ -196,23 +215,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
binding.tunnelList.setOnScrollListener(new CustomRecyclerViewScrollListener() {
|
||||
@Override
|
||||
public void show() {
|
||||
binding.createMenu.animate()
|
||||
.translationY(0)
|
||||
.setInterpolator(FAB_SHOW_ANIMATION)
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
binding.createMenu.animate()
|
||||
.translationY(binding.createMenu.getHeight() + getResources().getDimension(R.dimen.fab_margin))
|
||||
.setInterpolator(FAB_HIDE_ANIMATION)
|
||||
.start();
|
||||
}
|
||||
});
|
||||
binding.tunnelList.setOnScrollListener(new FloatingActionsMenuRecyclerViewScrollListener(binding.createMenu));
|
||||
binding.executePendingBindings();
|
||||
return binding.getRoot();
|
||||
}
|
||||
@@ -238,6 +241,17 @@ 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.setBeepEnabled(false);
|
||||
intentIntegrator.setPrompt(getString(R.string.qrcode_hint));
|
||||
intentIntegrator.initiateScan(Collections.singletonList(IntentIntegrator.QR_CODE));
|
||||
|
||||
if (binding != null)
|
||||
binding.createMenu.collapse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (binding != null) {
|
||||
@@ -299,7 +313,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
}
|
||||
|
||||
binding.setFragment(this);
|
||||
binding.setTunnels(Application.getTunnelManager().getTunnels());
|
||||
Application.getTunnelManager().getTunnels().thenAccept(binding::setTunnels);
|
||||
binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> {
|
||||
binding.setFragment(this);
|
||||
binding.getRoot().setOnClickListener(clicked -> {
|
||||
@@ -327,25 +341,29 @@ public class TunnelListFragment extends BaseFragment {
|
||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_action_delete:
|
||||
final Collection<Tunnel> tunnelsToDelete = new ArrayList<>();
|
||||
for (final Integer position : checkedItems) {
|
||||
tunnelsToDelete.add(Application.getTunnelManager().getTunnels().get(position));
|
||||
}
|
||||
final Iterable<Integer> copyCheckedItems = new HashSet<>(checkedItems);
|
||||
Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
|
||||
final Collection<Tunnel> tunnelsToDelete = new ArrayList<>();
|
||||
for (final Integer position : copyCheckedItems)
|
||||
tunnelsToDelete.add(tunnels.get(position));
|
||||
|
||||
final CompletableFuture[] futures = StreamSupport.stream(tunnelsToDelete)
|
||||
.map(Tunnel::delete)
|
||||
.toArray(CompletableFuture[]::new);
|
||||
CompletableFuture.allOf(futures)
|
||||
.thenApply(x -> futures.length)
|
||||
.whenComplete(TunnelListFragment.this::onTunnelDeletionFinished);
|
||||
final CompletableFuture[] futures = StreamSupport.stream(tunnelsToDelete)
|
||||
.map(Tunnel::delete)
|
||||
.toArray(CompletableFuture[]::new);
|
||||
CompletableFuture.allOf(futures)
|
||||
.thenApply(x -> futures.length)
|
||||
.whenComplete(TunnelListFragment.this::onTunnelDeletionFinished);
|
||||
|
||||
});
|
||||
checkedItems.clear();
|
||||
mode.finish();
|
||||
return true;
|
||||
case R.id.menu_action_select_all:
|
||||
for (int i = 0; i < Application.getTunnelManager().getTunnels().size(); ++i) {
|
||||
setItemChecked(i, true);
|
||||
}
|
||||
Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
|
||||
for (int i = 0; i < tunnels.size(); ++i) {
|
||||
setItemChecked(i, true);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.wireguard.android.configStore.ConfigStore;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.model.Tunnel.Statistics;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ObservableKeyedList;
|
||||
import com.wireguard.android.util.ObservableSortedKeyedArrayList;
|
||||
import com.wireguard.android.util.ObservableSortedKeyedList;
|
||||
import com.wireguard.config.Config;
|
||||
@@ -47,6 +46,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
|
||||
|
||||
private final ConfigStore configStore;
|
||||
private final CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> completableTunnels = new CompletableFuture<>();
|
||||
private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR);
|
||||
@Nullable private Tunnel lastUsedTunnel;
|
||||
private boolean haveLoaded;
|
||||
@@ -121,8 +121,8 @@ public final class TunnelManager extends BaseObservable {
|
||||
.thenApply(tunnel::onStatisticsChanged);
|
||||
}
|
||||
|
||||
public ObservableKeyedList<String, Tunnel> getTunnels() {
|
||||
return tunnels;
|
||||
public CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> getTunnels() {
|
||||
return completableTunnels;
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
@@ -152,6 +152,8 @@ public final class TunnelManager extends BaseObservable {
|
||||
f.completeExceptionally(t);
|
||||
}
|
||||
});
|
||||
|
||||
completableTunnels.complete(tunnels);
|
||||
}
|
||||
|
||||
public void refreshTunnelStates() {
|
||||
@@ -285,10 +287,12 @@ public final class TunnelManager extends BaseObservable {
|
||||
final String tunnelName = intent.getStringExtra("tunnel");
|
||||
if (tunnelName == null)
|
||||
return;
|
||||
final Tunnel tunnel = manager.getTunnels().get(tunnelName);
|
||||
if (tunnel == null)
|
||||
return;
|
||||
manager.setTunnelState(tunnel, state);
|
||||
manager.getTunnels().thenAccept(tunnels -> {
|
||||
final Tunnel tunnel = tunnels.get(tunnelName);
|
||||
if (tunnel == null)
|
||||
return;
|
||||
manager.setTunnelState(tunnel, state);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class VersionPreference extends Preference {
|
||||
public VersionPreference(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
Application.onHaveBackend(backend -> {
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
versionSummary = getContext().getString(R.string.version_summary_checking, backend.getTypeName().toLowerCase());
|
||||
Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete((version, exception) -> {
|
||||
versionSummary = exception == null
|
||||
|
||||
@@ -48,7 +48,10 @@ public class ZipExporterPreference extends Preference {
|
||||
}
|
||||
|
||||
private void exportZip() {
|
||||
final List<Tunnel> tunnels = new ArrayList<>(Application.getTunnelManager().getTunnels());
|
||||
Application.getTunnelManager().getTunnels().thenAccept(this::exportZip);
|
||||
}
|
||||
|
||||
private void exportZip(final List<Tunnel> tunnels) {
|
||||
final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
|
||||
for (final Tunnel tunnel : tunnels)
|
||||
futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture());
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.BuildConfig;
|
||||
import com.wireguard.android.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -45,8 +46,8 @@ public class RootShell {
|
||||
final File cacheDir = context.getCacheDir();
|
||||
localBinaryDir = new File(cacheDir, "bin");
|
||||
localTemporaryDir = new File(cacheDir, "tmp");
|
||||
preamble = String.format("export CALLING_PACKAGE=com.wireguard.android PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n",
|
||||
localBinaryDir, localTemporaryDir);
|
||||
preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n",
|
||||
BuildConfig.APPLICATION_ID, localBinaryDir, localTemporaryDir);
|
||||
}
|
||||
|
||||
private static boolean isExecutableInPath(final String name) {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2018 Harsh Shandilya <msfjarvis@gmail.com>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
|
||||
public abstract class CustomRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
|
||||
|
||||
private int scrollDist;
|
||||
private boolean isVisible = true;
|
||||
private static int flingThreshold;
|
||||
|
||||
@Override
|
||||
public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
|
||||
if (flingThreshold == 0)
|
||||
flingThreshold = recyclerView.getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal) / 2;
|
||||
|
||||
if (isVisible && scrollDist >= flingThreshold) {
|
||||
hide();
|
||||
scrollDist = 0;
|
||||
isVisible = false;
|
||||
} else if (!isVisible && scrollDist <= -flingThreshold) {
|
||||
show();
|
||||
scrollDist = 0;
|
||||
isVisible = true;
|
||||
}
|
||||
|
||||
if (isVisible ? dy > 0 : dy < 0) {
|
||||
scrollDist += dy;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void show();
|
||||
public abstract void hide();
|
||||
}
|
||||
+4
-4
@@ -13,8 +13,9 @@ import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionsMenu> {
|
||||
|
||||
public FloatingActionButtonBehavior(final Context context, final AttributeSet attrs) { }
|
||||
public FloatingActionButtonBehavior(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean layoutDependsOn(final CoordinatorLayout parent, final FloatingActionsMenu child,
|
||||
@@ -25,8 +26,7 @@ public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<Flo
|
||||
@Override
|
||||
public boolean onDependentViewChanged(final CoordinatorLayout parent, final FloatingActionsMenu child,
|
||||
final View dependency) {
|
||||
final float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
|
||||
child.setTranslationY(translationY);
|
||||
child.setBehaviorYTranslation(Math.min(0, dependency.getTranslationY() - dependency.getMeasuredHeight()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ public class FloatingActionsMenu extends ViewGroup {
|
||||
@Nullable private TouchDelegateGroup mTouchDelegateGroup;
|
||||
@Nullable private OnFloatingActionsMenuUpdateListener mListener;
|
||||
private final Rect touchArea = new Rect(0, 0, 0, 0);
|
||||
private float scrollYTranslation;
|
||||
private float behaviorYTranslation;
|
||||
|
||||
public FloatingActionsMenu(final Context context) {
|
||||
this(context, null);
|
||||
@@ -103,6 +105,24 @@ public class FloatingActionsMenu extends ViewGroup {
|
||||
createAddButton(context);
|
||||
}
|
||||
|
||||
public float getScrollYTranslation() {
|
||||
return scrollYTranslation;
|
||||
}
|
||||
|
||||
public void setScrollYTranslation(final float scrollYTranslation) {
|
||||
this.scrollYTranslation = scrollYTranslation;
|
||||
setTranslationY(behaviorYTranslation + scrollYTranslation);
|
||||
}
|
||||
|
||||
public float getBehaviorYTranslation() {
|
||||
return behaviorYTranslation;
|
||||
}
|
||||
|
||||
public void setBehaviorYTranslation(final float behaviorYTranslation) {
|
||||
this.behaviorYTranslation = behaviorYTranslation;
|
||||
setTranslationY(behaviorYTranslation + scrollYTranslation);
|
||||
}
|
||||
|
||||
public void setOnFloatingActionsMenuUpdateListener(final OnFloatingActionsMenuUpdateListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget.fab;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
public class FloatingActionsMenuRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
|
||||
private static final float SCALE_FACTOR = 1.5f;
|
||||
private final FloatingActionsMenu menu;
|
||||
|
||||
public FloatingActionsMenuRecyclerViewScrollListener(final FloatingActionsMenu menu) {
|
||||
this.menu = menu;
|
||||
}
|
||||
|
||||
private static float bound(final float min, final float proposal, final float max) {
|
||||
return Math.min(max, Math.max(min, proposal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
menu.setScrollYTranslation(bound(0, menu.getScrollYTranslation() + dy * SCALE_FACTOR, menu.getMeasuredHeight() - menu.getTranslationY()));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -71,7 +70,7 @@ public enum Attribute {
|
||||
}
|
||||
|
||||
public String composeWith(final int value) {
|
||||
return String.format(Locale.getDefault(), "%s = %d%n", token, value);
|
||||
return String.format("%s = %d%n", token, value);
|
||||
}
|
||||
|
||||
public <T> String composeWith(final Iterable<T> value) {
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import java9.lang.Iterables;
|
||||
|
||||
@@ -72,7 +71,7 @@ public class Peer {
|
||||
private String getEndpointString() {
|
||||
if (endpoint == null)
|
||||
return null;
|
||||
return String.format(Locale.getDefault(), "%s:%d", endpoint.getHostString(), endpoint.getPort());
|
||||
return String.format("%s:%d", endpoint.getHostString(), endpoint.getPort());
|
||||
}
|
||||
|
||||
public int getPersistentKeepalive() {
|
||||
@@ -104,12 +103,10 @@ public class Peer {
|
||||
if (endpoint.isUnresolved())
|
||||
throw new UnknownHostException(endpoint.getHostString());
|
||||
if (endpoint.getAddress() instanceof Inet6Address)
|
||||
return String.format(Locale.getDefault(),
|
||||
"[%s]:%d",
|
||||
return String.format("[%s]:%d",
|
||||
endpoint.getAddress().getHostAddress(),
|
||||
endpoint.getPort());
|
||||
return String.format(Locale.getDefault(),
|
||||
"%s:%d",
|
||||
return String.format("%s:%d",
|
||||
endpoint.getAddress().getHostAddress(),
|
||||
endpoint.getPort());
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package com.wireguard.util;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,8 +20,10 @@ public interface KeyedList<K, E extends Keyed<? extends K>> extends List<E> {
|
||||
|
||||
boolean containsKey(K key);
|
||||
|
||||
@Nullable
|
||||
E get(K key);
|
||||
|
||||
@Nullable
|
||||
E getLast(K key);
|
||||
|
||||
int indexOfKey(K key);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package com.wireguard.util;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
@@ -17,10 +19,12 @@ import java.util.Set;
|
||||
public interface SortedKeyedList<K, E extends Keyed<? extends K>> extends KeyedList<K, E> {
|
||||
Comparator<? super K> comparator();
|
||||
|
||||
@Nullable
|
||||
K firstKey();
|
||||
|
||||
Set<K> keySet();
|
||||
|
||||
@Nullable
|
||||
K lastKey();
|
||||
|
||||
Collection<E> values();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M4,4H10V10H4V4M20,4V10H14V4H20M14,15H16V13H14V11H16V13H18V11H20V13H18V15H20V18H18V20H16V18H13V20H11V16H14V15M16,15V18H18V15H16M4,20V14H10V20H4M6,6V8H8V6H6M16,6V8H18V6H16M6,16V18H8V16H6M4,11H6V13H4V11M9,11H13V15H11V13H9V11M11,6H13V10H11V6M2,2V6H0V2A2,2 0 0,1 2,0H6V2H2M22,0A2,2 0 0,1 24,2V6H22V2H18V0H22M2,18V22H6V24H2A2,2 0 0,1 0,22V18H2M22,22V18H24V22A2,2 0 0,1 22,24H18V22H22Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/tunnel_name_text_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/tunnel_name_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/tunnel_name" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</layout>
|
||||
@@ -55,7 +55,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toStartOf="@+id/tunnel_switch"
|
||||
android:text="@string/interface_title" />
|
||||
|
||||
<com.wireguard.android.widget.ToggleSwitch
|
||||
|
||||
@@ -30,33 +30,48 @@
|
||||
android:id="@+id/tunnel_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/design_fab_size_normal"
|
||||
android:paddingBottom="@{@dimen/design_fab_size_normal * 1.1f}"
|
||||
android:clipToPadding="false"
|
||||
android:choiceMode="multipleChoiceModal"
|
||||
android:visibility="@{tunnels.size() > 0 ? android.view.View.VISIBLE : android.view.View.GONE}"
|
||||
app:items="@{tunnels}"
|
||||
app:layout="@{@layout/tunnel_list_item}"
|
||||
app:configurationHandler="@{rowConfigurationHandler}" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="@{tunnels.size() == 0 ? android.view.View.VISIBLE : android.view.View.GONE}"
|
||||
android:layout_gravity="center">
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/logo_placeholder"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="140dp"
|
||||
android:layout_gravity="center"
|
||||
android:tint="@color/transparent_background_placeholder"
|
||||
android:layout_marginTop="-70dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/tunnel_list_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.wireguard.android.widget.fab.FloatingActionsMenu
|
||||
android:id="@+id/create_menu"
|
||||
android:clipChildren="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:fab_labelStyle="@style/fab_label"
|
||||
android:clipChildren="false"
|
||||
app:fab_labelsPosition="left"
|
||||
app:fab_labelsPosition="@integer/label_position"
|
||||
app:layout_behavior="com.wireguard.android.widget.fab.FloatingActionButtonBehavior" >
|
||||
|
||||
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
|
||||
android:id="@+id/create_empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{fragment::onRequestCreateConfig}"
|
||||
app:fabSize="mini"
|
||||
app:srcCompat="@drawable/ic_action_edit_white"
|
||||
app:fab_title="@string/create_empty" />
|
||||
|
||||
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
|
||||
android:id="@+id/create_from_file"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -65,6 +80,25 @@
|
||||
app:srcCompat="@drawable/ic_action_open_white"
|
||||
app:fabSize="mini"
|
||||
app:fab_title="@string/create_from_file" />
|
||||
|
||||
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
|
||||
android:id="@+id/scan_qr_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{fragment::onRequestScanQRCode}"
|
||||
app:srcCompat="@drawable/ic_action_scan_qr_code_white"
|
||||
app:fabSize="mini"
|
||||
app:fab_title="@string/create_from_qrcode" />
|
||||
|
||||
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
|
||||
android:id="@+id/create_empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{fragment::onRequestCreateConfig}"
|
||||
app:fabSize="mini"
|
||||
app:srcCompat="@drawable/ic_action_edit_white"
|
||||
app:fab_title="@string/create_empty" />
|
||||
</com.wireguard.android.widget.fab.FloatingActionsMenu>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toStartOf="@+id/tunnel_switch"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@{key}" />
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="label_position">1</integer>
|
||||
</resources>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="android:navigationBarColor">?android:attr/colorBackground</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -7,4 +7,7 @@
|
||||
<color name="fab_label_background_color">#bbbbbb</color>
|
||||
|
||||
<color name="accent">#5e97f6</color>
|
||||
|
||||
<!-- TODO(msf): replace with ?android:attr/colorBackground with some alpha -->
|
||||
<color name="transparent_background_placeholder">#aa2e2e2e</color>
|
||||
</resources>
|
||||
|
||||
@@ -7,4 +7,7 @@
|
||||
<color name="fab_label_background_color">#444444</color>
|
||||
|
||||
<color name="accent">#2196F3</color>
|
||||
|
||||
<!-- TODO(msf): replace with ?android:attr/colorBackground with some alpha -->
|
||||
<color name="transparent_background_placeholder">#aafafafa</color>
|
||||
</resources>
|
||||
|
||||
@@ -27,4 +27,5 @@
|
||||
<enum name="right" value="3"/>
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
<integer name="label_position">0</integer>
|
||||
</resources>
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
<string name="create_activity_title">Create WireGuard Tunnel</string>
|
||||
<string name="create_empty">Create from scratch</string>
|
||||
<string name="create_from_file">Create from file or archive</string>
|
||||
<string name="create_from_qrcode">Create from QR code</string>
|
||||
<string name="create_tunnel">Create Tunnel</string>
|
||||
<string name="dark_theme_title">Use dark theme</string>
|
||||
<string name="dark_theme_summary_on">Currently using dark night theme</string>
|
||||
<string name="dark_theme_summary_off">Currently using light day theme</string>
|
||||
@@ -50,6 +52,7 @@
|
||||
<string name="hint_generated">(generated)</string>
|
||||
<string name="hint_optional">(optional)</string>
|
||||
<string name="hint_random">(random)</string>
|
||||
<string name="import_from_qrcode">Import Tunnel from QR Code</string>
|
||||
<string name="import_error">Unable to import tunnel: %s</string>
|
||||
<string name="import_success">Imported “%s”</string>
|
||||
<string name="interface_title">Interface</string>
|
||||
@@ -68,6 +71,7 @@
|
||||
<string name="private_key">Private key</string>
|
||||
<string name="public_key">Public key</string>
|
||||
<string name="public_key_description">Public key</string>
|
||||
<string name="qrcode_hint">Tip: generate with `qrencode -t ansiutf8 < tunnel.conf`.</string>
|
||||
<string name="restore_on_boot_summary">Bring up previously-enabled tunnels on boot</string>
|
||||
<string name="restore_on_boot_title">Restore on boot</string>
|
||||
<string name="save">Save</string>
|
||||
@@ -89,6 +93,8 @@
|
||||
<string name="tools_installer_title">Install command line tools</string>
|
||||
<string name="tunnel_create_error">Unable to create tunnel: %s</string>
|
||||
<string name="tunnel_create_success">Successfully created tunnel “%s”</string>
|
||||
<string name="tunnel_list_placeholder">Add a tunnel using the blue button</string>
|
||||
<string name="tunnel_name">Tunnel Name</string>
|
||||
<string name="tunnel_rename_error">Unable to rename tunnel: %s</string>
|
||||
<string name="tunnel_rename_success">Successfully renamed tunnel to “%s”</string>
|
||||
<string name="version_title">WireGuard for Android v%s"</string>
|
||||
|
||||
@@ -8,12 +8,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
# Work around https://github.com/android-ndk/ndk/issues/602
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
|
||||
|
||||
add_executable(libwg-quick.so wireguard/src/tools/wg-quick/android.c)
|
||||
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -pedantic -Wno-missing-field-initializers)
|
||||
add_executable(libwg-quick.so wireguard/src/tools/wg-quick/android.c ndk-compat/compat.c)
|
||||
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -pedantic -Wno-missing-field-initializers -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
|
||||
|
||||
file(GLOB WG_SOURCES wireguard/src/tools/*.c libmnl/src/*.c)
|
||||
file(GLOB WG_SOURCES wireguard/src/tools/*.c libmnl/src/*.c ndk-compat/compat.c)
|
||||
add_executable(libwg.so ${WG_SOURCES})
|
||||
target_compile_options(libwg.so PUBLIC -idirafter "${CMAKE_CURRENT_SOURCE_DIR}/libmnl/include/" -I "${CMAKE_CURRENT_SOURCE_DIR}/wireguard/src/tools/" -O3 -std=gnu11 -D_GNU_SOURCE -DHAVE_VISIBILITY_HIDDEN -DRUNSTATEDIR=\"\\\"/data/data/com.wireguard.android/cache\\\"\" -Wno-pointer-arith -Wno-unused-parameter)
|
||||
target_compile_options(libwg.so PUBLIC -idirafter "${CMAKE_CURRENT_SOURCE_DIR}/libmnl/include/" -I "${CMAKE_CURRENT_SOURCE_DIR}/wireguard/src/tools/" -O3 -std=gnu11 -D_GNU_SOURCE -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DHAVE_VISIBILITY_HIDDEN -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\" -Wno-pointer-arith -Wno-unused-parameter)
|
||||
|
||||
add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND make
|
||||
ANDROID_ARCH_NAME=${ANDROID_ARCH_NAME}
|
||||
@@ -21,6 +21,7 @@ add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib
|
||||
ANDROID_TOOLCHAIN_ROOT=${ANDROID_TOOLCHAIN_ROOT}
|
||||
ANDROID_LLVM_TRIPLE=${ANDROID_LLVM_TRIPLE}
|
||||
ANDROID_SYSROOT=${ANDROID_SYSROOT}
|
||||
ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME}
|
||||
CFLAGS=${CMAKE_C_FLAGS}\ -Wno-unused-command-line-argument
|
||||
LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}\ -fuse-ld=gold
|
||||
DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
|
||||
@@ -54,5 +54,5 @@ $(DESTDIR)/libwg-go.so: $(FILES) src/git.zx2c4.com/wireguard-go/api-android.go s
|
||||
mkdir -p $(subst ../wireguard-go/,./src/git.zx2c4.com/wireguard-go/,$(dir $(FILES)))
|
||||
$(foreach FILE,$(FILES),ln -sfrt $(subst ../wireguard-go/,./src/git.zx2c4.com/wireguard-go/,$(dir $(FILE))) $(FILE);)
|
||||
GOPATH=$(PWD) go get -v -d git.zx2c4.com/wireguard-go
|
||||
GOPATH=$(PWD) go build -v -o $(DESTDIR)/libwg-go.so -buildmode c-shared git.zx2c4.com/wireguard-go
|
||||
GOPATH=$(PWD) go build -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o $(DESTDIR)/libwg-go.so -buildmode c-shared git.zx2c4.com/wireguard-go
|
||||
go version > .gobuildversion
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
@@ -33,11 +34,16 @@ func (l AndroidLogger) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
var tunnelHandles map[int32]*Device
|
||||
type TunnelHandle struct {
|
||||
device *Device
|
||||
uapi net.Listener
|
||||
}
|
||||
|
||||
var tunnelHandles map[int32]TunnelHandle
|
||||
|
||||
func init() {
|
||||
roamingDisabled = true
|
||||
tunnelHandles = make(map[int32]*Device)
|
||||
tunnelHandles = make(map[int32]TunnelHandle)
|
||||
signals := make(chan os.Signal)
|
||||
signal.Notify(signals, unix.SIGUSR2)
|
||||
go func() {
|
||||
@@ -85,6 +91,29 @@ func wgTurnOn(ifnameRef string, tun_fd int32, settings string) int32 {
|
||||
return -1
|
||||
}
|
||||
|
||||
uapiFile, err := UAPIOpen(name)
|
||||
if err != nil {
|
||||
unix.Close(int(tun_fd))
|
||||
logger.Error.Println(err)
|
||||
return -1
|
||||
}
|
||||
uapi, err := UAPIListen(name, uapiFile)
|
||||
if err != nil {
|
||||
uapiFile.Close()
|
||||
unix.Close(int(tun_fd))
|
||||
logger.Error.Println(err)
|
||||
return -1
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go ipcHandle(device, conn)
|
||||
}
|
||||
}()
|
||||
|
||||
device.Up()
|
||||
logger.Info.Println("Device started")
|
||||
|
||||
@@ -98,27 +127,28 @@ func wgTurnOn(ifnameRef string, tun_fd int32, settings string) int32 {
|
||||
unix.Close(int(tun_fd))
|
||||
return -1
|
||||
}
|
||||
tunnelHandles[i] = device
|
||||
tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi}
|
||||
return i
|
||||
}
|
||||
|
||||
//export wgTurnOff
|
||||
func wgTurnOff(tunnelHandle int32) {
|
||||
device, ok := tunnelHandles[tunnelHandle]
|
||||
handle, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delete(tunnelHandles, tunnelHandle)
|
||||
device.Close()
|
||||
handle.uapi.Close()
|
||||
handle.device.Close()
|
||||
}
|
||||
|
||||
//export wgGetSocketV4
|
||||
func wgGetSocketV4(tunnelHandle int32) int32 {
|
||||
device, ok := tunnelHandles[tunnelHandle]
|
||||
handle, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
native, ok := device.net.bind.(*NativeBind)
|
||||
native, ok := handle.device.net.bind.(*NativeBind)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
@@ -138,11 +168,11 @@ func wgGetSocketV4(tunnelHandle int32) int32 {
|
||||
|
||||
//export wgGetSocketV6
|
||||
func wgGetSocketV6(tunnelHandle int32) int32 {
|
||||
device, ok := tunnelHandles[tunnelHandle]
|
||||
handle, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
native, ok := device.net.bind.(*NativeBind)
|
||||
native, ok := handle.device.net.bind.(*NativeBind)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: BSD
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FILE_IS_EMPTY
|
||||
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
|
||||
#undef FILE_IS_EMPTY
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
|
||||
{
|
||||
char *ptr, *eptr;
|
||||
|
||||
if (*buf == NULL || *bufsiz == 0) {
|
||||
*bufsiz = BUFSIZ;
|
||||
if ((*buf = malloc(*bufsiz)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
||||
int c = fgetc(fp);
|
||||
if (c == -1) {
|
||||
if (feof(fp)) {
|
||||
ssize_t diff = (ssize_t)(ptr - *buf);
|
||||
if (diff != 0) {
|
||||
*ptr = '\0';
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
*ptr++ = c;
|
||||
if (c == delimiter) {
|
||||
*ptr = '\0';
|
||||
return ptr - *buf;
|
||||
}
|
||||
if (ptr + 2 >= eptr) {
|
||||
char *nbuf;
|
||||
size_t nbufsiz = *bufsiz * 2;
|
||||
ssize_t d = ptr - *buf;
|
||||
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
|
||||
return -1;
|
||||
*buf = nbuf;
|
||||
*bufsiz = nbufsiz;
|
||||
eptr = nbuf + nbufsiz;
|
||||
ptr = nbuf + d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
|
||||
{
|
||||
return getdelim(buf, bufsiz, '\n', fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
|
||||
#undef FILE_IS_EMPTY
|
||||
#include <string.h>
|
||||
|
||||
char *strchrnul(const char *s, int c)
|
||||
{
|
||||
char *x = strchr(s, c);
|
||||
if (!x)
|
||||
return (char *)s + strlen(s);
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FILE_IS_EMPTY
|
||||
#undef FILE_IS_EMPTY
|
||||
static char ____x __attribute__((unused));
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: BSD
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
|
||||
#include <stdio.h>
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
|
||||
char *strchrnul(const char *s, int c);
|
||||
#endif
|
||||
|
||||
+1
-1
Submodule app/tools/wireguard updated: 80b41cd845...8f49f7b30f
+1
-1
Submodule app/tools/wireguard-go updated: 6b3b1c3b91...3ad3e83c7a
Reference in New Issue
Block a user