global: Add nullity annotations

Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
Eric Kuck 2018-07-12 19:10:35 -05:00 committed by Jason A. Donenfeld
parent fbaa4d9ab1
commit 67ea8b2936
48 changed files with 480 additions and 309 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ build/
*.iml *.iml
*.jks *.jks
keystore.properties keystore.properties
package-info.java

View File

@ -94,6 +94,8 @@
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML> </XML>
<codeStyleSettings language="JAVA"> <codeStyleSettings language="JAVA">
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
<arrangement> <arrangement>
<groups /> <groups />
<rules> <rules>

View File

@ -1,4 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply from: 'nonnull.gradle'
// Create a variable called keystorePropertiesFile, and initialize it to your // Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder. // keystore.properties file, in the rootProject folder.
@ -56,6 +57,7 @@ ext {
databindingVersion = '3.1.3' databindingVersion = '3.1.3'
supportLibsVersion = '27.1.1' supportLibsVersion = '27.1.1'
streamsupportVersion = '1.6.0' streamsupportVersion = '1.6.0'
jsr305Version = '3.0.2'
} }
dependencies { dependencies {
@ -67,6 +69,7 @@ dependencies {
implementation "com.android.support:support-annotations:$supportLibsVersion" implementation "com.android.support:support-annotations:$supportLibsVersion"
implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion" implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion"
implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion" implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion"
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {

87
app/nonnull.gradle Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright © 2018 Eric Kuck <eric@bluelinelabs.com>.
* SPDX-License-Identifier: Apache-2.0
*/
task generateNonNullJavaFiles(dependsOn: "assembleDebug", type: Copy) {
group = "Copying"
description = "Generate package-info.java classes"
def basePackage = "com" + File.separatorChar + "wireguard"
def mainSrcPhrase = "src" + File.separatorChar + "main" + File.separatorChar +
"java" + File.separatorChar
def mainTestSrcPhrase = "src" + File.separatorChar + "test" + File.separatorChar +
"java" + File.separatorChar
def mainAndroidTestSrcPhrase = "src" + File.separatorChar + "androidTest" + File.separatorChar +
"java" + File.separatorChar
def sourceDir = file( "${projectDir}" + File.separatorChar + "src" + File.separatorChar +
"main" + File.separatorChar + "java" + File.separatorChar +
basePackage )
def testSourceDir = file( "${projectDir}" + File.separatorChar + "src" + File.separatorChar +
"test" + File.separatorChar + "java" + File.separatorChar +
basePackage)
def androidTestSourceDir = file( "${projectDir}" + File.separatorChar + "src" + File
.separatorChar +
"androidTest" + File.separatorChar + "java" + File.separatorChar +
basePackage )
generateInfoFiles(sourceDir, mainSrcPhrase);
sourceDir.eachDirRecurse { dir ->
generateInfoFiles(dir, mainSrcPhrase)
}
if (file(testSourceDir).exists()) {
generateInfoFiles(testSourceDir, mainTestSrcPhrase);
testSourceDir.eachDirRecurse { dir ->
generateInfoFiles(dir, mainTestSrcPhrase)
}
}
if (file(androidTestSourceDir).exists()) {
generateInfoFiles(androidTestSourceDir, mainAndroidTestSrcPhrase);
androidTestSourceDir.eachDirRecurse { dir ->
generateInfoFiles(dir, mainAndroidTestSrcPhrase)
}
}
println "[SUCCESS] NonNull generator: package-info.java files checked"
}
private void generateInfoFiles(File dir, String mainSrcPhrase) {
def infoFileContentHeader = getFileContentHeader();
def infoFileContentFooter = getFileContentFooter();
def infoFilePath = dir.getAbsolutePath() + File.separatorChar + "package-info.java"
//file(infoFilePath).delete(); //do not use in production code
if (!file(infoFilePath).exists()) {
def infoFileContentPackage = getFileContentPackage(dir.getAbsolutePath(), mainSrcPhrase);
new File(infoFilePath).write(infoFileContentHeader +
infoFileContentPackage + infoFileContentFooter)
println "[dir] " + infoFilePath + " created";
}
}
def getFileContentPackage(String path, String mainSrcPhrase) {
def mainSrcPhraseIndex = path.indexOf(mainSrcPhrase)
def output = path.substring(mainSrcPhraseIndex)
// Win hotfix
if (System.properties['os.name'].toLowerCase().contains('windows')) {
output = output.replace("\\", "/")
mainSrcPhrase = mainSrcPhrase.replace("\\", "/")
}
return "package " + output.replaceAll(mainSrcPhrase, "").replaceAll(
"/", ".") + ";\n"
}
def getFileContentHeader() {
return "/**\n" +
" * Make all method parameters @NonNull by default.\n" +
" */\n" +
"@NonNullForAll\n"
}
def getFileContentFooter() {
return "\n" +
"import com.wireguard.util.NonNullForAll;\n"
}

View File

@ -10,6 +10,7 @@ import android.os.AsyncTask;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import com.wireguard.android.backend.Backend; import com.wireguard.android.backend.Backend;
@ -26,18 +27,19 @@ 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.Collection; import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
public class Application extends android.app.Application { public class Application extends android.app.Application {
private static WeakReference<Application> weakSelf; @SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
private AsyncWorker asyncWorker; @SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
private Backend backend; @SuppressWarnings("NullableProblems") private RootShell rootShell;
private RootShell rootShell; @SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
private SharedPreferences sharedPreferences; @SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
private ToolsInstaller toolsInstaller; @SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
private TunnelManager tunnelManager; @SuppressWarnings("NullableProblems") private Handler handler;
private Handler handler; @Nullable private Backend backend;
private Collection<BackendCallback> haveBackendCallbacks = new ArrayList<>(); @Nullable private Collection<BackendCallback> haveBackendCallbacks = new ArrayList<>();
private final Object haveBackendCallbacksLock = new Object(); private final Object haveBackendCallbacksLock = new Object();
public Application() { public Application() {
@ -65,9 +67,11 @@ public class Application extends android.app.Application {
if (app.backend == null) if (app.backend == null)
app.backend = new GoBackend(app.getApplicationContext()); app.backend = new GoBackend(app.getApplicationContext());
synchronized (app.haveBackendCallbacksLock) { synchronized (app.haveBackendCallbacksLock) {
for (final BackendCallback callback : app.haveBackendCallbacks) if (app.haveBackendCallbacks != null) {
app.handler.post(() -> callback.callback(app.backend)); for (final BackendCallback callback : app.haveBackendCallbacks)
app.haveBackendCallbacks = null; app.handler.post(() -> callback.callback(app.backend));
app.haveBackendCallbacks = null;
}
} }
} }
return app.backend; return app.backend;
@ -82,10 +86,12 @@ public class Application extends android.app.Application {
public static void onHaveBackend(final BackendCallback callback) { public static void onHaveBackend(final BackendCallback callback) {
final Application app = get(); final Application app = get();
synchronized (app.haveBackendCallbacksLock) { synchronized (app.haveBackendCallbacksLock) {
if (app.haveBackendCallbacks == null) if (app.haveBackendCallbacks == null) {
Objects.requireNonNull(app.backend, "Backend still null in onHaveBackend call");
callback.callback(app.backend); callback.callback(app.backend);
else } else {
app.haveBackendCallbacks.add(callback); app.haveBackendCallbacks.add(callback);
}
} }
} }

View File

@ -12,12 +12,11 @@ import android.databinding.Observable;
import android.databinding.Observable.OnPropertyChangedCallback; import android.databinding.Observable.OnPropertyChangedCallback;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.os.Build; import android.os.Build;
import android.service.quicksettings.Tile; import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService; import android.service.quicksettings.TileService;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
@ -41,9 +40,9 @@ public class QuickTileService extends TileService {
private final OnStateChangedCallback onStateChangedCallback = new OnStateChangedCallback(); private final OnStateChangedCallback onStateChangedCallback = new OnStateChangedCallback();
private final OnTunnelChangedCallback onTunnelChangedCallback = new OnTunnelChangedCallback(); private final OnTunnelChangedCallback onTunnelChangedCallback = new OnTunnelChangedCallback();
private Tunnel tunnel; @Nullable private Tunnel tunnel;
private Icon iconOn; @Nullable private Icon iconOn;
private Icon iconOff; @Nullable private Icon iconOff;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
@ -91,7 +90,7 @@ public class QuickTileService extends TileService {
} }
private void onToggleFinished(@SuppressWarnings("unused") final State state, private void onToggleFinished(@SuppressWarnings("unused") final State state,
final Throwable throwable) { @Nullable final Throwable throwable) {
if (throwable == null) if (throwable == null)
return; return;
final String error = ExceptionLoggers.unwrapMessage(throwable); final String error = ExceptionLoggers.unwrapMessage(throwable);

View File

@ -9,6 +9,7 @@ package com.wireguard.android.activity;
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 android.support.annotation.Nullable;
import com.wireguard.android.Application; import com.wireguard.android.Application;
import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel;
@ -24,19 +25,19 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel"; private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry(); private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
private Tunnel selectedTunnel; @Nullable private Tunnel selectedTunnel;
public void addOnSelectedTunnelChangedListener( public void addOnSelectedTunnelChangedListener(final OnSelectedTunnelChangedListener listener) {
final OnSelectedTunnelChangedListener listener) {
selectionChangeRegistry.add(listener); selectionChangeRegistry.add(listener);
} }
@Nullable
public Tunnel getSelectedTunnel() { public Tunnel getSelectedTunnel() {
return selectedTunnel; return selectedTunnel;
} }
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
// Restore the saved tunnel if there is one; otherwise grab it from the arguments. // Restore the saved tunnel if there is one; otherwise grab it from the arguments.
String savedTunnelName = null; String savedTunnelName = null;
if (savedInstanceState != null) if (savedInstanceState != null)
@ -59,14 +60,14 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
protected abstract void onSelectedTunnelChanged(Tunnel oldTunnel, Tunnel newTunnel); protected abstract void onSelectedTunnelChanged(@Nullable Tunnel oldTunnel, @Nullable Tunnel newTunnel);
public void removeOnSelectedTunnelChangedListener( public void removeOnSelectedTunnelChangedListener(
final OnSelectedTunnelChangedListener listener) { final OnSelectedTunnelChangedListener listener) {
selectionChangeRegistry.remove(listener); selectionChangeRegistry.remove(listener);
} }
public void setSelectedTunnel(final Tunnel tunnel) { public void setSelectedTunnel(@Nullable final Tunnel tunnel) {
final Tunnel oldTunnel = selectedTunnel; final Tunnel oldTunnel = selectedTunnel;
if (Objects.equals(oldTunnel, tunnel)) if (Objects.equals(oldTunnel, tunnel))
return; return;
@ -76,7 +77,7 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
} }
public interface OnSelectedTunnelChangedListener { public interface OnSelectedTunnelChangedListener {
void onSelectedTunnelChanged(Tunnel oldTunnel, Tunnel newTunnel); void onSelectedTunnelChanged(@Nullable Tunnel oldTunnel, @Nullable Tunnel newTunnel);
} }
private static final class SelectionChangeNotifier private static final class SelectionChangeNotifier

View File

@ -9,6 +9,7 @@ package com.wireguard.android.activity;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
@ -22,6 +23,8 @@ import com.wireguard.android.fragment.TunnelEditorFragment;
import com.wireguard.android.fragment.TunnelListFragment; import com.wireguard.android.fragment.TunnelListFragment;
import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel;
import java.util.List;
import java9.util.stream.Stream; import java9.util.stream.Stream;
/** /**
@ -33,6 +36,7 @@ import java9.util.stream.Stream;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
private static final String KEY_STATE = "fragment_state"; private static final String KEY_STATE = "fragment_state";
private static final String TAG = "WireGuard/" + MainActivity.class.getSimpleName(); private static final String TAG = "WireGuard/" + MainActivity.class.getSimpleName();
private State state = State.EMPTY; private State state = State.EMPTY;
private boolean moveToState(final State nextState) { private boolean moveToState(final State nextState) {
@ -70,13 +74,19 @@ public class MainActivity extends BaseActivity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
TunnelListFragment fragment = null; final List<Fragment> fragments = getSupportFragmentManager().getFragments();
try {
fragment = ((TunnelListFragment) getSupportFragmentManager().getFragments().get(0)); boolean handled = false;
} catch (final ClassCastException ignored) { } if (!fragments.isEmpty() && fragments.get(0) instanceof TunnelListFragment) {
if (fragment == null || !fragment.collapseActionMenu()) { handled = ((TunnelListFragment) fragments.get(0)).collapseActionMenu();
if (!moveToState(State.ofLayer(state.layer - 1))) }
super.onBackPressed();
if (!handled) {
handled = moveToState(State.ofLayer(state.layer - 1));
}
if (!handled) {
super.onBackPressed();
} }
} }
@ -84,7 +94,7 @@ public class MainActivity extends BaseActivity {
// calling View#performClick defeats the purpose of it. // calling View#performClick defeats the purpose of it.
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity); setContentView(R.layout.main_activity);
if (savedInstanceState != null && savedInstanceState.getString(KEY_STATE) != null) if (savedInstanceState != null && savedInstanceState.getString(KEY_STATE) != null)
@ -99,9 +109,10 @@ public class MainActivity extends BaseActivity {
final int actionBarId = getResources().getIdentifier("action_bar", "id", getPackageName()); final int actionBarId = getResources().getIdentifier("action_bar", "id", getPackageName());
if (actionBarId != 0 && findViewById(actionBarId) != null) { if (actionBarId != 0 && findViewById(actionBarId) != null) {
findViewById(actionBarId).setOnTouchListener((v, e) -> { findViewById(actionBarId).setOnTouchListener((v, e) -> {
try { final List<Fragment> fragments = getSupportFragmentManager().getFragments();
((TunnelListFragment) getSupportFragmentManager().getFragments().get(0)).collapseActionMenu(); if (!fragments.isEmpty() && fragments.get(0) instanceof TunnelListFragment) {
} catch (final ClassCastException ignored) { } ((TunnelListFragment) fragments.get(0)).collapseActionMenu();
}
return false; return false;
}); });
} }
@ -142,7 +153,7 @@ public class MainActivity extends BaseActivity {
} }
@Override @Override
protected void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { protected void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
moveToState(newTunnel != null ? State.DETAIL : State.LIST); moveToState(newTunnel != null ? State.DETAIL : State.LIST);
} }
@ -157,10 +168,10 @@ public class MainActivity extends BaseActivity {
DETAIL(TunnelDetailFragment.class, 2), DETAIL(TunnelDetailFragment.class, 2),
EDITOR(TunnelEditorFragment.class, 3); EDITOR(TunnelEditorFragment.class, 3);
private final String fragment; @Nullable private final String fragment;
private final int layer; private final int layer;
State(final Class<? extends Fragment> fragment, final int layer) { State(@Nullable final Class<? extends Fragment> fragment, final int layer) {
this.fragment = fragment != null ? fragment.getName() : null; this.fragment = fragment != null ? fragment.getName() : null;
this.layer = layer; this.layer = layer;
} }

View File

@ -8,12 +8,13 @@ package com.wireguard.android.activity;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat; import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import android.view.MenuItem; import android.view.MenuItem;
import com.wireguard.android.Application; import com.wireguard.android.Application;
@ -22,16 +23,14 @@ import com.wireguard.android.backend.WgQuickBackend;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Interface for changing application-global persistent settings. * Interface for changing application-global persistent settings.
*/ */
public class SettingsActivity extends ThemeChangeAwareActivity { public class SettingsActivity extends ThemeChangeAwareActivity {
private final Map<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>(); private final SparseArray<PermissionRequestCallback> permissionRequestCallbacks = new SparseArray<>();
private int permissionRequestCounter; private int permissionRequestCounter;
public void ensurePermissions(final String[] permissions, final PermissionRequestCallback cb) { public void ensurePermissions(final String[] permissions, final PermissionRequestCallback cb) {
@ -54,7 +53,7 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
} }
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) { if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
@ -76,8 +75,8 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
@Override @Override
public void onRequestPermissionsResult(final int requestCode, public void onRequestPermissionsResult(final int requestCode,
@NonNull final String[] permissions, final String[] permissions,
@NonNull final int[] grantResults) { final int[] grantResults) {
final PermissionRequestCallback f = permissionRequestCallbacks.get(requestCode); final PermissionRequestCallback f = permissionRequestCallbacks.get(requestCode);
if (f != null) { if (f != null) {
permissionRequestCallbacks.remove(requestCode); permissionRequestCallbacks.remove(requestCode);

View File

@ -8,6 +8,7 @@ package com.wireguard.android.activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import android.util.Log; import android.util.Log;
@ -19,7 +20,7 @@ import java.lang.reflect.Field;
public abstract class ThemeChangeAwareActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener { public abstract class ThemeChangeAwareActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "WireGuard/" + ThemeChangeAwareActivity.class.getSimpleName(); private static final String TAG = "WireGuard/" + ThemeChangeAwareActivity.class.getSimpleName();
private static Resources lastResources; @Nullable private static Resources lastResources;
private static boolean lastDarkMode; private static boolean lastDarkMode;
private static synchronized void invalidateDrawableCache(final Resources resources, final boolean darkMode) { private static synchronized void invalidateDrawableCache(final Resources resources, final boolean darkMode) {
if (resources == lastResources && darkMode == lastDarkMode) if (resources == lastResources && darkMode == lastDarkMode)
@ -51,7 +52,7 @@ public abstract class ThemeChangeAwareActivity extends AppCompatActivity impleme
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
} }

View File

@ -7,6 +7,7 @@
package com.wireguard.android.activity; package com.wireguard.android.activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import com.wireguard.android.fragment.TunnelEditorFragment; import com.wireguard.android.fragment.TunnelEditorFragment;
import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel;
@ -18,7 +19,7 @@ import com.wireguard.android.model.Tunnel;
public class TunnelCreatorActivity extends BaseActivity { public class TunnelCreatorActivity extends BaseActivity {
@Override @Override
@SuppressWarnings("UnnecessaryFullyQualifiedName") @SuppressWarnings("UnnecessaryFullyQualifiedName")
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) { if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
@ -28,7 +29,7 @@ public class TunnelCreatorActivity extends BaseActivity {
} }
@Override @Override
protected void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { protected void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
finish(); finish();
} }
} }

View File

@ -10,6 +10,7 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.support.annotation.Nullable;
import android.support.v4.util.ArraySet; import android.support.v4.util.ArraySet;
import android.util.Log; import android.util.Log;
@ -29,6 +30,7 @@ import com.wireguard.crypto.KeyEncoding;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Collections; import java.util.Collections;
import java.util.Formatter; import java.util.Formatter;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -40,7 +42,7 @@ public final class GoBackend implements Backend {
private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>(); private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
private final Context context; private final Context context;
private Tunnel currentTunnel; @Nullable private Tunnel currentTunnel;
private int currentTunnelHandle = -1; private int currentTunnelHandle = -1;
public GoBackend(final Context context) { public GoBackend(final Context context) {
@ -114,12 +116,14 @@ public final class GoBackend implements Backend {
return getState(tunnel); return getState(tunnel);
} }
private void setStateInternal(final Tunnel tunnel, final Config config, final State state) private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state)
throws Exception { throws Exception {
if (state == State.UP) { if (state == State.UP) {
Log.i(TAG, "Bringing tunnel up"); Log.i(TAG, "Bringing tunnel up");
Objects.requireNonNull(config, "Trying to bring up a tunnel with no config");
if (VpnService.prepare(context) != null) if (VpnService.prepare(context) != null)
throw new Exception("VPN service not authorized by user"); throw new Exception("VPN service not authorized by user");
@ -245,7 +249,7 @@ public final class GoBackend implements Backend {
} }
@Override @Override
public int onStartCommand(final Intent intent, final int flags, final int startId) { public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
vpnService.complete(this); vpnService.complete(this);
if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) { if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) {
Log.d(TAG, "Service started by Always-on VPN feature"); Log.d(TAG, "Service started by Always-on VPN feature");

View File

@ -7,6 +7,7 @@
package com.wireguard.android.backend; package com.wireguard.android.backend;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.wireguard.android.Application; import com.wireguard.android.Application;
@ -21,6 +22,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java9.util.stream.Collectors; import java9.util.stream.Collectors;
@ -106,8 +108,9 @@ public final class WgQuickBackend implements Backend {
return getState(tunnel); return getState(tunnel);
} }
private void setStateInternal(final Tunnel tunnel, final Config config, final State state) private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) throws Exception {
throws Exception { Objects.requireNonNull(config, "Trying to set state with a null config");
final File tempFile = new File(localTemporaryDir, tunnel.getName() + ".conf"); final File tempFile = new File(localTemporaryDir, tunnel.getName() + ".conf");
try (final FileOutputStream stream = new FileOutputStream(tempFile, false)) { try (final FileOutputStream stream = new FileOutputStream(tempFile, false)) {
stream.write(config.toString().getBytes(StandardCharsets.UTF_8)); stream.write(config.toString().getBytes(StandardCharsets.UTF_8));

View File

@ -9,6 +9,7 @@ package com.wireguard.android.databinding;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.databinding.ObservableList; import android.databinding.ObservableList;
import android.databinding.ViewDataBinding; import android.databinding.ViewDataBinding;
import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -16,6 +17,7 @@ import android.view.ViewGroup;
import com.wireguard.android.BR; import com.wireguard.android.BR;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects;
/** /**
* Helper class for binding an ObservableList to the children of a ViewGroup. * Helper class for binding an ObservableList to the children of a ViewGroup.
@ -26,7 +28,7 @@ class ItemChangeListener<T> {
private final ViewGroup container; private final ViewGroup container;
private final int layoutId; private final int layoutId;
private final LayoutInflater layoutInflater; private final LayoutInflater layoutInflater;
private ObservableList<T> list; @Nullable private ObservableList<T> list;
ItemChangeListener(final ViewGroup container, final int layoutId) { ItemChangeListener(final ViewGroup container, final int layoutId) {
this.container = container; this.container = container;
@ -34,17 +36,21 @@ class ItemChangeListener<T> {
layoutInflater = LayoutInflater.from(container.getContext()); layoutInflater = LayoutInflater.from(container.getContext());
} }
private View getView(final int position, final View convertView) { private View getView(final int position, @Nullable final View convertView) {
ViewDataBinding binding = DataBindingUtil.getBinding(convertView); ViewDataBinding binding = convertView != null ? DataBindingUtil.getBinding(convertView) : null;
if (binding == null) if (binding == null) {
binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false); binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false);
}
Objects.requireNonNull(list, "Trying to get a view while list is still null");
binding.setVariable(BR.collection, list); binding.setVariable(BR.collection, list);
binding.setVariable(BR.item, list.get(position)); binding.setVariable(BR.item, list.get(position));
binding.executePendingBindings(); binding.executePendingBindings();
return binding.getRoot(); return binding.getRoot();
} }
void setList(final ObservableList<T> newList) { void setList(@Nullable final ObservableList<T> newList) {
if (list != null) if (list != null)
list.removeOnListChangedCallback(callback); list.removeOnListChangedCallback(callback);
list = newList; list = newList;

View File

@ -10,7 +10,7 @@ import android.content.Context;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.databinding.ObservableList; import android.databinding.ObservableList;
import android.databinding.ViewDataBinding; import android.databinding.ViewDataBinding;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
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;
@ -31,8 +31,8 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
private final OnListChangedCallback<E> callback = new OnListChangedCallback<>(this); private final OnListChangedCallback<E> callback = new OnListChangedCallback<>(this);
private final int layoutId; private final int layoutId;
private final LayoutInflater layoutInflater; private final LayoutInflater layoutInflater;
private ObservableKeyedList<K, E> list; @Nullable private ObservableKeyedList<K, E> list;
private RowConfigurationHandler rowConfigurationHandler; @Nullable private RowConfigurationHandler rowConfigurationHandler;
ObservableKeyedRecyclerViewAdapter(final Context context, final int layoutId, ObservableKeyedRecyclerViewAdapter(final Context context, final int layoutId,
final ObservableKeyedList<K, E> list) { final ObservableKeyedList<K, E> list) {
@ -46,6 +46,7 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
return list != null ? list.size() : 0; return list != null ? list.size() : 0;
} }
@Nullable
private E getItem(final int position) { private E getItem(final int position) {
if (list == null || position < 0 || position >= list.size()) if (list == null || position < 0 || position >= list.size())
return null; return null;
@ -58,30 +59,34 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
return key != null ? key.hashCode() : -1; return key != null ? key.hashCode() : -1;
} }
@Nullable
private K getKey(final int position) { private K getKey(final int position) {
final E item = getItem(position); final E item = getItem(position);
return item != null ? item.getKey() : null; return item != null ? item.getKey() : null;
} }
@NonNull @Override @Override
public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
return new ViewHolder(DataBindingUtil.inflate(layoutInflater, layoutId, parent, false)); return new ViewHolder(DataBindingUtil.inflate(layoutInflater, layoutId, parent, false));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) { public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.binding.setVariable(BR.collection, list); holder.binding.setVariable(BR.collection, list);
holder.binding.setVariable(BR.key, getKey(position)); holder.binding.setVariable(BR.key, getKey(position));
holder.binding.setVariable(BR.item, getItem(position)); holder.binding.setVariable(BR.item, getItem(position));
holder.binding.executePendingBindings(); holder.binding.executePendingBindings();
if (rowConfigurationHandler != null) { if (rowConfigurationHandler != null) {
rowConfigurationHandler.onConfigureRow(holder.binding, getItem(position), position); E item = getItem(position);
if (item != null) {
rowConfigurationHandler.onConfigureRow(holder.binding, item, position);
}
} }
} }
void setList(final ObservableKeyedList<K, E> newList) { void setList(@Nullable final ObservableKeyedList<K, E> newList) {
if (list != null) if (list != null)
list.removeOnListChangedCallback(callback); list.removeOnListChangedCallback(callback);
list = newList; list = newList;

View File

@ -39,7 +39,7 @@ public class AppListDialogFragment extends DialogFragment {
private static final String KEY_EXCLUDED_APPS = "excludedApps"; private static final String KEY_EXCLUDED_APPS = "excludedApps";
private List<String> currentlyExcludedApps; private final List<String> currentlyExcludedApps = Arrays.asList(getArguments().getStringArray(KEY_EXCLUDED_APPS));
private final ObservableKeyedList<String, ApplicationData> appData = new ObservableKeyedArrayList<>(); private final ObservableKeyedList<String, ApplicationData> appData = new ObservableKeyedArrayList<>();
public static <T extends Fragment & AppExclusionListener> AppListDialogFragment newInstance(final String[] excludedApps, final T target) { public static <T extends Fragment & AppExclusionListener> AppListDialogFragment newInstance(final String[] excludedApps, final T target) {
@ -51,24 +51,12 @@ public class AppListDialogFragment extends DialogFragment {
return fragment; return fragment;
} }
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currentlyExcludedApps = Arrays.asList(getArguments().getStringArray(KEY_EXCLUDED_APPS));
}
@Override
public void onAttach(final Context context) {
super.onAttach(context);
}
@Override @Override
public Dialog onCreateDialog(final Bundle savedInstanceState) { public Dialog onCreateDialog(final Bundle savedInstanceState) {
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
alertDialogBuilder.setTitle(R.string.excluded_applications); alertDialogBuilder.setTitle(R.string.excluded_applications);
AppListDialogFragmentBinding binding = AppListDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false); final AppListDialogFragmentBinding binding = AppListDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false);
binding.executePendingBindings(); binding.executePendingBindings();
alertDialogBuilder.setView(binding.getRoot()); alertDialogBuilder.setView(binding.getRoot());

View File

@ -10,7 +10,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding; import android.databinding.ViewDataBinding;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log; import android.util.Log;
@ -37,10 +37,11 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
private static final String TAG = "WireGuard/" + BaseFragment.class.getSimpleName(); private static final String TAG = "WireGuard/" + BaseFragment.class.getSimpleName();
private static final int REQUEST_CODE_VPN_PERMISSION = 23491; private static final int REQUEST_CODE_VPN_PERMISSION = 23491;
private BaseActivity activity; @Nullable private BaseActivity activity;
private Tunnel pendingTunnel; @Nullable private Tunnel pendingTunnel;
private Boolean pendingTunnelUp; @Nullable private Boolean pendingTunnelUp;
@Nullable
protected Tunnel getSelectedTunnel() { protected Tunnel getSelectedTunnel() {
return activity != null ? activity.getSelectedTunnel() : null; return activity != null ? activity.getSelectedTunnel() : null;
} }
@ -65,7 +66,7 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
} }
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_VPN_PERMISSION) { if (requestCode == REQUEST_CODE_VPN_PERMISSION) {
@ -76,7 +77,7 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
} }
} }
protected void setSelectedTunnel(final Tunnel tunnel) { protected void setSelectedTunnel(@Nullable final Tunnel tunnel) {
if (activity != null) if (activity != null)
activity.setSelectedTunnel(tunnel); activity.setSelectedTunnel(tunnel);
} }
@ -106,7 +107,7 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
}); });
} }
private void setTunnelStateWithPermissionsResult(@NonNull final Tunnel tunnel, final boolean checked) { private void setTunnelStateWithPermissionsResult(final Tunnel tunnel, final boolean checked) {
tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> { tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> {
if (throwable == null) if (throwable == null)
return; return;

View File

@ -7,7 +7,7 @@
package com.wireguard.android.fragment; package com.wireguard.android.fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -24,14 +24,16 @@ import com.wireguard.config.Config;
*/ */
public class TunnelDetailFragment extends BaseFragment { public class TunnelDetailFragment extends BaseFragment {
private TunnelDetailFragmentBinding binding; @Nullable private TunnelDetailFragmentBinding binding;
private void onConfigLoaded(final String name, final Config config) { private void onConfigLoaded(final String name, final Config config) {
binding.setConfig(new Config.Observable(config, name)); if (binding != null) {
binding.setConfig(new Config.Observable(config, name));
}
} }
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -42,8 +44,8 @@ public class TunnelDetailFragment extends BaseFragment {
} }
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
binding = TunnelDetailFragmentBinding.inflate(inflater, container, false); binding = TunnelDetailFragmentBinding.inflate(inflater, container, false);
binding.executePendingBindings(); binding.executePendingBindings();
@ -57,7 +59,7 @@ public class TunnelDetailFragment extends BaseFragment {
} }
@Override @Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { public void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
if (binding == null) if (binding == null)
return; return;
binding.setTunnel(newTunnel); binding.setTunnel(newTunnel);
@ -68,7 +70,11 @@ public class TunnelDetailFragment extends BaseFragment {
} }
@Override @Override
public void onViewStateRestored(final Bundle savedInstanceState) { public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
if (binding == null) {
return;
}
binding.setFragment(this); binding.setFragment(this);
onSelectedTunnelChanged(null, getSelectedTunnel()); onSelectedTunnelChanged(null, getSelectedTunnel());
super.onViewStateRestored(savedInstanceState); super.onViewStateRestored(savedInstanceState);

View File

@ -11,7 +11,7 @@ import android.content.Context;
import android.databinding.Observable; import android.databinding.Observable;
import android.databinding.ObservableList; import android.databinding.ObservableList;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.util.Log; import android.util.Log;
@ -37,6 +37,7 @@ import com.wireguard.config.Config;
import com.wireguard.config.Peer; import com.wireguard.config.Peer;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* Fragment for editing a WireGuard configuration. * Fragment for editing a WireGuard configuration.
@ -47,15 +48,17 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
private static final String KEY_ORIGINAL_NAME = "original_name"; private static final String KEY_ORIGINAL_NAME = "original_name";
private static final String TAG = "WireGuard/" + TunnelEditorFragment.class.getSimpleName(); private static final String TAG = "WireGuard/" + TunnelEditorFragment.class.getSimpleName();
private TunnelEditorFragmentBinding binding; @Nullable private TunnelEditorFragmentBinding binding;
private Tunnel tunnel; @Nullable private Tunnel tunnel;
private void onConfigLoaded(final String name, final Config config) { private void onConfigLoaded(final String name, final Config config) {
binding.setConfig(new Config.Observable(config, name)); if (binding != null) {
binding.setConfig(new Config.Observable(config, name));
}
} }
private void onConfigSaved(final Tunnel savedTunnel, private void onConfigSaved(final Tunnel savedTunnel,
final Throwable throwable) { @Nullable final Throwable throwable) {
final String message; final String message;
if (throwable == null) { if (throwable == null) {
message = getString(R.string.config_save_success, savedTunnel.getName()); message = getString(R.string.config_save_success, savedTunnel.getName());
@ -73,7 +76,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
} }
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -124,8 +127,8 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
}; };
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
binding = TunnelEditorFragmentBinding.inflate(inflater, container, false); binding = TunnelEditorFragmentBinding.inflate(inflater, container, false);
binding.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler); binding.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
@ -197,14 +200,14 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
} }
@Override @Override
public void onSaveInstanceState(@NonNull final Bundle outState) { public void onSaveInstanceState(final Bundle outState) {
outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig()); outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig());
outState.putString(KEY_ORIGINAL_NAME, tunnel == null ? null : tunnel.getName()); outState.putString(KEY_ORIGINAL_NAME, tunnel == null ? null : tunnel.getName());
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@Override @Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { public void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
tunnel = newTunnel; tunnel = newTunnel;
if (binding == null) if (binding == null)
return; return;
@ -213,7 +216,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
tunnel.getConfigAsync().thenAccept(a -> onConfigLoaded(tunnel.getName(), a)); tunnel.getConfigAsync().thenAccept(a -> onConfigLoaded(tunnel.getName(), a));
} }
private void onTunnelCreated(final Tunnel newTunnel, final Throwable throwable) { private void onTunnelCreated(final Tunnel newTunnel, @Nullable final Throwable throwable) {
final String message; final String message;
if (throwable == null) { if (throwable == null) {
tunnel = newTunnel; tunnel = newTunnel;
@ -232,7 +235,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
} }
private void onTunnelRenamed(final Tunnel renamedTunnel, final Config newConfig, private void onTunnelRenamed(final Tunnel renamedTunnel, final Config newConfig,
final Throwable throwable) { @Nullable final Throwable throwable) {
final String message; final String message;
if (throwable == null) { if (throwable == null) {
message = getString(R.string.tunnel_rename_success, renamedTunnel.getName()); message = getString(R.string.tunnel_rename_success, renamedTunnel.getName());
@ -251,7 +254,11 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
} }
@Override @Override
public void onViewStateRestored(final Bundle savedInstanceState) { public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
if (binding == null) {
return;
}
binding.setFragment(this); binding.setFragment(this);
if (savedInstanceState == null) { if (savedInstanceState == null) {
@ -271,15 +278,16 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
public void onRequestSetExcludedApplications(@SuppressWarnings("unused") final View view) { public void onRequestSetExcludedApplications(@SuppressWarnings("unused") final View view) {
final FragmentManager fragmentManager = getFragmentManager(); final FragmentManager fragmentManager = getFragmentManager();
if (fragmentManager != null) { if (fragmentManager != null && binding != null) {
final String[] excludedApps = Attribute.stringToList(binding.getConfig().getInterfaceSection().getExcludedApplications()); final String[] excludedApps = Attribute.stringToList(binding.getConfig().getInterfaceSection().getExcludedApplications());
final AppListDialogFragment fragment = AppListDialogFragment.newInstance(excludedApps, this); final AppListDialogFragment fragment = AppListDialogFragment.newInstance(excludedApps, this);
fragment.show(getFragmentManager(), null); fragment.show(fragmentManager, null);
} }
} }
@Override @Override
public void onExcludedAppsSelected(final List<String> excludedApps) { public void onExcludedAppsSelected(final List<String> excludedApps) {
Objects.requireNonNull(binding, "Tried to set excluded apps while no view was loaded");
binding.getConfig().getInterfaceSection().setExcludedApplications(Attribute.iterableToString(excludedApps)); binding.getConfig().getInterfaceSection().setExcludedApplications(Attribute.iterableToString(excludedApps));
} }

View File

@ -15,7 +15,6 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
@ -60,20 +59,20 @@ public class TunnelListFragment extends BaseFragment {
private static final String TAG = "WireGuard/" + TunnelListFragment.class.getSimpleName(); private static final String TAG = "WireGuard/" + TunnelListFragment.class.getSimpleName();
private final ActionModeListener actionModeListener = new ActionModeListener(); private final ActionModeListener actionModeListener = new ActionModeListener();
private ActionMode actionMode; @Nullable private ActionMode actionMode;
private TunnelListFragmentBinding binding; @Nullable private TunnelListFragmentBinding binding;
public boolean collapseActionMenu() { public boolean collapseActionMenu() {
if (binding.createMenu.isExpanded()) { if (binding != null && binding.createMenu.isExpanded()) {
binding.createMenu.collapse(); binding.createMenu.collapse();
return true; return true;
} }
return false; return false;
} }
private void importTunnel(final Uri uri) { private void importTunnel(@Nullable final Uri uri) {
final Activity activity = getActivity(); final Activity activity = getActivity();
if (activity == null) if (activity == null || uri == null)
return; return;
final ContentResolver contentResolver = activity.getContentResolver(); final ContentResolver contentResolver = activity.getContentResolver();
@ -165,10 +164,10 @@ public class TunnelListFragment extends BaseFragment {
} }
@Override @Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
switch (requestCode) { switch (requestCode) {
case REQUEST_IMPORT: case REQUEST_IMPORT:
if (resultCode == Activity.RESULT_OK) if (resultCode == Activity.RESULT_OK && data != null)
importTunnel(data.getData()); importTunnel(data.getData());
return; return;
default: default:
@ -178,8 +177,8 @@ public class TunnelListFragment extends BaseFragment {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
binding = TunnelListFragmentBinding.inflate(inflater, container, false); binding = TunnelListFragmentBinding.inflate(inflater, container, false);
@ -216,16 +215,18 @@ public class TunnelListFragment extends BaseFragment {
@Override @Override
public void onPause() { public void onPause() {
binding.createMenu.collapse(); if (binding != null) {
binding.createMenu.collapse();
}
super.onPause(); super.onPause();
} }
@Override @Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { public void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
// Do nothing. // Do nothing.
} }
private void onTunnelDeletionFinished(final Integer count, final Throwable throwable) { private void onTunnelDeletionFinished(final Integer count, @Nullable final Throwable throwable) {
final String message; final String message;
if (throwable == null) { if (throwable == null) {
message = getResources().getQuantityString(R.plurals.delete_success, count, count); message = getResources().getQuantityString(R.plurals.delete_success, count, count);
@ -265,8 +266,13 @@ public class TunnelListFragment extends BaseFragment {
} }
@Override @Override
public void onViewStateRestored(final Bundle savedInstanceState) { public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState); super.onViewStateRestored(savedInstanceState);
if (binding == null) {
return;
}
binding.setFragment(this); binding.setFragment(this);
binding.setTunnels(Application.getTunnelManager().getTunnels()); binding.setTunnels(Application.getTunnelManager().getTunnels());
binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> { binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> {
@ -290,7 +296,7 @@ public class TunnelListFragment extends BaseFragment {
private final class ActionModeListener implements ActionMode.Callback { private final class ActionModeListener implements ActionMode.Callback {
private final Collection<Integer> checkedItems = new HashSet<>(); private final Collection<Integer> checkedItems = new HashSet<>();
private Resources resources; @Nullable private Resources resources;
@Override @Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
@ -357,7 +363,9 @@ public class TunnelListFragment extends BaseFragment {
actionMode.finish(); actionMode.finish();
} }
binding.tunnelList.getAdapter().notifyItemChanged(position); if (binding != null) {
binding.tunnelList.getAdapter().notifyItemChanged(position);
}
updateTitle(actionMode); updateTitle(actionMode);
} }

View File

@ -9,36 +9,32 @@ package com.wireguard.android.model;
import android.databinding.BaseObservable; import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import com.wireguard.android.BR; import com.wireguard.android.BR;
import com.wireguard.util.Keyed; import com.wireguard.util.Keyed;
public class ApplicationData extends BaseObservable implements Keyed<String> { public class ApplicationData extends BaseObservable implements Keyed<String> {
@NonNull private final Drawable icon; private final Drawable icon;
@NonNull private final String name; private final String name;
@NonNull private final String packageName; private final String packageName;
private boolean excludedFromTunnel; private boolean excludedFromTunnel;
public ApplicationData(@NonNull final Drawable icon, @NonNull final String name, @NonNull final String packageName, final boolean excludedFromTunnel) { public ApplicationData(final Drawable icon, final String name, final String packageName, final boolean excludedFromTunnel) {
this.icon = icon; this.icon = icon;
this.name = name; this.name = name;
this.packageName = packageName; this.packageName = packageName;
this.excludedFromTunnel = excludedFromTunnel; this.excludedFromTunnel = excludedFromTunnel;
} }
@NonNull
public Drawable getIcon() { public Drawable getIcon() {
return icon; return icon;
} }
@NonNull
public String getName() { public String getName() {
return name; return name;
} }
@NonNull
public String getPackageName() { public String getPackageName() {
return packageName; return packageName;
} }

View File

@ -8,13 +8,12 @@ package com.wireguard.android.model;
import android.databinding.BaseObservable; import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.wireguard.android.BR; import com.wireguard.android.BR;
import com.wireguard.android.util.ExceptionLoggers; import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.util.Keyed;
import com.wireguard.config.Config; import com.wireguard.config.Config;
import com.wireguard.util.Keyed;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -30,20 +29,20 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}"); private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}");
private final TunnelManager manager; private final TunnelManager manager;
private Config config; @Nullable private Config config;
private String name; private String name;
private State state; private State state;
private Statistics statistics; @Nullable private Statistics statistics;
Tunnel(@NonNull final TunnelManager manager, @NonNull final String name, Tunnel(final TunnelManager manager, final String name,
@Nullable final Config config, @NonNull final State state) { @Nullable final Config config, final State state) {
this.manager = manager; this.manager = manager;
this.name = name; this.name = name;
this.config = config; this.config = config;
this.state = state; this.state = state;
} }
public static boolean isNameInvalid(@NonNull final CharSequence name) { public static boolean isNameInvalid(final CharSequence name) {
return !NAME_PATTERN.matcher(name).matches(); return !NAME_PATTERN.matcher(name).matches();
} }
@ -51,7 +50,7 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
return manager.delete(this); return manager.delete(this);
} }
@Bindable @Bindable @Nullable
public Config getConfig() { public Config getConfig() {
if (config == null) if (config == null)
manager.getTunnelConfig(this).whenComplete(ExceptionLoggers.E); manager.getTunnelConfig(this).whenComplete(ExceptionLoggers.E);
@ -83,7 +82,7 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
return TunnelManager.getTunnelState(this); return TunnelManager.getTunnelState(this);
} }
@Bindable @Bindable @Nullable
public Statistics getStatistics() { public Statistics getStatistics() {
// FIXME: Check age of statistics. // FIXME: Check age of statistics.
if (statistics == null) if (statistics == null)
@ -118,25 +117,26 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
return state; return state;
} }
Statistics onStatisticsChanged(final Statistics statistics) { @Nullable
Statistics onStatisticsChanged(@Nullable final Statistics statistics) {
this.statistics = statistics; this.statistics = statistics;
notifyPropertyChanged(BR.statistics); notifyPropertyChanged(BR.statistics);
return statistics; return statistics;
} }
public CompletionStage<Config> setConfig(@NonNull final Config config) { public CompletionStage<Config> setConfig(final Config config) {
if (!config.equals(this.config)) if (!config.equals(this.config))
return manager.setTunnelConfig(this, config); return manager.setTunnelConfig(this, config);
return CompletableFuture.completedFuture(this.config); return CompletableFuture.completedFuture(this.config);
} }
public CompletionStage<String> setName(@NonNull final String name) { public CompletionStage<String> setName(final String name) {
if (!name.equals(this.name)) if (!name.equals(this.name))
return manager.setTunnelName(this, name); return manager.setTunnelName(this, name);
return CompletableFuture.completedFuture(this.name); return CompletableFuture.completedFuture(this.name);
} }
public CompletionStage<State> setState(@NonNull final State state) { public CompletionStage<State> setState(final State state) {
if (state != this.state) if (state != this.state)
return manager.setTunnelState(this, state); return manager.setTunnelState(this, state);
return CompletableFuture.completedFuture(this.state); return CompletableFuture.completedFuture(this.state);
@ -152,6 +152,5 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
} }
} }
public static class Statistics extends BaseObservable { public static class Statistics extends BaseObservable { }
}
} }

View File

@ -11,7 +11,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.databinding.BaseObservable; import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import com.wireguard.android.Application; import com.wireguard.android.Application;
import com.wireguard.android.BR; import com.wireguard.android.BR;
@ -47,9 +47,8 @@ public final class TunnelManager extends BaseObservable {
private static final String KEY_RUNNING_TUNNELS = "enabled_configs"; private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
private final ConfigStore configStore; private final ConfigStore configStore;
private final ObservableSortedKeyedList<String, Tunnel> tunnels = private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR);
new ObservableSortedKeyedArrayList<>(COMPARATOR); @Nullable private Tunnel lastUsedTunnel;
private Tunnel lastUsedTunnel;
private boolean haveLoaded; private boolean haveLoaded;
private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>(); private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
@ -57,13 +56,13 @@ public final class TunnelManager extends BaseObservable {
this.configStore = configStore; this.configStore = configStore;
} }
private Tunnel addToList(final String name, final Config config, final State state) { private Tunnel addToList(final String name, @Nullable final Config config, final State state) {
final Tunnel tunnel = new Tunnel(this, name, config, state); final Tunnel tunnel = new Tunnel(this, name, config, state);
tunnels.add(tunnel); tunnels.add(tunnel);
return tunnel; return tunnel;
} }
public CompletionStage<Tunnel> create(@NonNull final String name, final Config config) { public CompletionStage<Tunnel> create(final String name, @Nullable final Config config) {
if (Tunnel.isNameInvalid(name)) if (Tunnel.isNameInvalid(name))
return CompletableFuture.failedFuture(new IllegalArgumentException("Invalid name")); return CompletableFuture.failedFuture(new IllegalArgumentException("Invalid name"));
if (tunnels.containsKey(name)) { if (tunnels.containsKey(name)) {
@ -102,7 +101,7 @@ public final class TunnelManager extends BaseObservable {
}); });
} }
@Bindable @Bindable @Nullable
public Tunnel getLastUsedTunnel() { public Tunnel getLastUsedTunnel() {
return lastUsedTunnel; return lastUsedTunnel;
} }
@ -191,7 +190,7 @@ public final class TunnelManager extends BaseObservable {
Application.getSharedPreferences().edit().putStringSet(KEY_RUNNING_TUNNELS, runningTunnels).apply(); Application.getSharedPreferences().edit().putStringSet(KEY_RUNNING_TUNNELS, runningTunnels).apply();
} }
private void setLastUsedTunnel(final Tunnel tunnel) { private void setLastUsedTunnel(@Nullable final Tunnel tunnel) {
if (tunnel == lastUsedTunnel) if (tunnel == lastUsedTunnel)
return; return;
lastUsedTunnel = tunnel; lastUsedTunnel = tunnel;
@ -256,7 +255,7 @@ public final class TunnelManager extends BaseObservable {
public static final class IntentReceiver extends BroadcastReceiver { public static final class IntentReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(final Context context, final Intent intent) { public void onReceive(final Context context, @Nullable final Intent intent) {
final TunnelManager manager = Application.getTunnelManager(); final TunnelManager manager = Application.getTunnelManager();
if (intent == null) if (intent == null)
return; return;

View File

@ -10,6 +10,7 @@ import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -33,7 +34,7 @@ import java.io.InputStreamReader;
public class LogExporterPreference extends Preference { public class LogExporterPreference extends Preference {
private static final String TAG = "WireGuard/" + LogExporterPreference.class.getSimpleName(); private static final String TAG = "WireGuard/" + LogExporterPreference.class.getSimpleName();
private String exportedFilePath; @Nullable private String exportedFilePath;
public LogExporterPreference(final Context context, final AttributeSet attrs) { public LogExporterPreference(final Context context, final AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -73,7 +74,7 @@ public class LogExporterPreference extends Preference {
}).whenComplete(this::exportLogComplete); }).whenComplete(this::exportLogComplete);
} }
private void exportLogComplete(final String filePath, final Throwable throwable) { private void exportLogComplete(final String filePath, @Nullable final Throwable throwable) {
if (throwable != null) { if (throwable != null) {
final String error = ExceptionLoggers.unwrapMessage(throwable); final String error = ExceptionLoggers.unwrapMessage(throwable);
final String message = getContext().getString(R.string.log_export_error, error); final String message = getContext().getString(R.string.log_export_error, error);

View File

@ -7,7 +7,7 @@
package com.wireguard.android.preference; package com.wireguard.android.preference;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -43,7 +43,7 @@ public class ToolsInstallerPreference extends Preference {
Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::areInstalled).whenComplete(this::onCheckResult); Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::areInstalled).whenComplete(this::onCheckResult);
} }
private void onCheckResult(final int state, final Throwable throwable) { private void onCheckResult(final int state, @Nullable final Throwable throwable) {
if (throwable != null || state == ToolsInstaller.ERROR) if (throwable != null || state == ToolsInstaller.ERROR)
setState(State.INITIAL); setState(State.INITIAL);
else if ((state & ToolsInstaller.YES) == ToolsInstaller.YES) else if ((state & ToolsInstaller.YES) == ToolsInstaller.YES)
@ -62,7 +62,7 @@ public class ToolsInstallerPreference extends Preference {
Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::install).whenComplete(this::onInstallResult); Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::install).whenComplete(this::onInstallResult);
} }
private void onInstallResult(final Integer result, final Throwable throwable) { private void onInstallResult(final Integer result, @Nullable final Throwable throwable) {
if (throwable != null) if (throwable != null)
setState(State.FAILURE); setState(State.FAILURE);
else if ((result & (ToolsInstaller.YES | ToolsInstaller.MAGISK)) == (ToolsInstaller.YES | ToolsInstaller.MAGISK)) else if ((result & (ToolsInstaller.YES | ToolsInstaller.MAGISK)) == (ToolsInstaller.YES | ToolsInstaller.MAGISK))
@ -73,7 +73,7 @@ public class ToolsInstallerPreference extends Preference {
setState(State.FAILURE); setState(State.FAILURE);
} }
private void setState(@NonNull final State state) { private void setState(final State state) {
if (this.state == state) if (this.state == state)
return; return;
this.state = state; this.state = state;

View File

@ -9,6 +9,7 @@ import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -17,7 +18,7 @@ import com.wireguard.android.BuildConfig;
import com.wireguard.android.R; import com.wireguard.android.R;
public class VersionPreference extends Preference { public class VersionPreference extends Preference {
private String versionSummary; @Nullable private String versionSummary;
public VersionPreference(final Context context, final AttributeSet attrs) { public VersionPreference(final Context context, final AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -33,7 +34,7 @@ public class VersionPreference extends Preference {
}); });
} }
@Override @Override @Nullable
public CharSequence getSummary() { public CharSequence getSummary() {
return versionSummary; return versionSummary;
} }

View File

@ -10,6 +10,7 @@ import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -40,7 +41,7 @@ import java9.util.concurrent.CompletableFuture;
public class ZipExporterPreference extends Preference { public class ZipExporterPreference extends Preference {
private static final String TAG = "WireGuard/" + ZipExporterPreference.class.getSimpleName(); private static final String TAG = "WireGuard/" + ZipExporterPreference.class.getSimpleName();
private String exportedFilePath; @Nullable private String exportedFilePath;
public ZipExporterPreference(final Context context, final AttributeSet attrs) { public ZipExporterPreference(final Context context, final AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -79,7 +80,7 @@ public class ZipExporterPreference extends Preference {
}).whenComplete(this::exportZipComplete)); }).whenComplete(this::exportZipComplete));
} }
private void exportZipComplete(final String filePath, final Throwable throwable) { private void exportZipComplete(@Nullable final String filePath, @Nullable final Throwable throwable) {
if (throwable != null) { if (throwable != null) {
final String error = ExceptionLoggers.unwrapMessage(throwable); final String error = ExceptionLoggers.unwrapMessage(throwable);
final String message = getContext().getString(R.string.zip_export_error, error); final String message = getContext().getString(R.string.zip_export_error, error);

View File

@ -6,8 +6,9 @@
package com.wireguard.android.util; package com.wireguard.android.util;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import java9.util.concurrent.CompletionException; import java9.util.concurrent.CompletionException;
import java9.util.function.BiConsumer; import java9.util.function.BiConsumer;
@ -34,7 +35,6 @@ public enum ExceptionLoggers implements BiConsumer<Object, Throwable> {
return throwable; return throwable;
} }
@NonNull
public static String unwrapMessage(Throwable throwable) { public static String unwrapMessage(Throwable throwable) {
throwable = unwrap(throwable); throwable = unwrap(throwable);
final String message = throwable.getMessage(); final String message = throwable.getMessage();
@ -44,7 +44,7 @@ public enum ExceptionLoggers implements BiConsumer<Object, Throwable> {
} }
@Override @Override
public void accept(final Object result, final Throwable throwable) { public void accept(final Object result, @Nullable final Throwable throwable) {
if (throwable != null) if (throwable != null)
Log.println(Log.ERROR, TAG, Log.getStackTraceString(throwable)); Log.println(Log.ERROR, TAG, Log.getStackTraceString(throwable));
else if (priority <= Log.DEBUG) else if (priority <= Log.DEBUG)

View File

@ -7,7 +7,7 @@
package com.wireguard.android.util; package com.wireguard.android.util;
import android.databinding.ObservableArrayList; import android.databinding.ObservableArrayList;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import com.wireguard.util.Keyed; import com.wireguard.util.Keyed;
@ -25,28 +25,28 @@ import java.util.Objects;
public class ObservableKeyedArrayList<K, E extends Keyed<? extends K>> public class ObservableKeyedArrayList<K, E extends Keyed<? extends K>>
extends ObservableArrayList<E> implements ObservableKeyedList<K, E> { extends ObservableArrayList<E> implements ObservableKeyedList<K, E> {
@Override @Override
public boolean add(final E e) { public boolean add(@Nullable final E e) {
if (e == null) if (e == null)
throw new NullPointerException("Trying to add a null element"); throw new NullPointerException("Trying to add a null element");
return super.add(e); return super.add(e);
} }
@Override @Override
public void add(final int index, final E e) { public void add(final int index, @Nullable final E e) {
if (e == null) if (e == null)
throw new NullPointerException("Trying to add a null element"); throw new NullPointerException("Trying to add a null element");
super.add(index, e); super.add(index, e);
} }
@Override @Override
public boolean addAll(@NonNull final Collection<? extends E> c) { public boolean addAll(final Collection<? extends E> c) {
if (c.contains(null)) if (c.contains(null))
throw new NullPointerException("Trying to add a collection with null element(s)"); throw new NullPointerException("Trying to add a collection with null element(s)");
return super.addAll(c); return super.addAll(c);
} }
@Override @Override
public boolean addAll(final int index, @NonNull final Collection<? extends E> c) { public boolean addAll(final int index, final Collection<? extends E> c) {
if (c.contains(null)) if (c.contains(null))
throw new NullPointerException("Trying to add a collection with null element(s)"); throw new NullPointerException("Trying to add a collection with null element(s)");
return super.addAll(index, c); return super.addAll(index, c);
@ -65,13 +65,13 @@ public class ObservableKeyedArrayList<K, E extends Keyed<? extends K>>
return indexOfKey(key) >= 0; return indexOfKey(key) >= 0;
} }
@Override @Override @Nullable
public E get(final K key) { public E get(final K key) {
final int index = indexOfKey(key); final int index = indexOfKey(key);
return index >= 0 ? get(index) : null; return index >= 0 ? get(index) : null;
} }
@Override @Override @Nullable
public E getLast(final K key) { public E getLast(final K key) {
final int index = lastIndexOfKey(key); final int index = lastIndexOfKey(key);
return index >= 0 ? get(index) : null; return index >= 0 ? get(index) : null;
@ -100,7 +100,7 @@ public class ObservableKeyedArrayList<K, E extends Keyed<? extends K>>
} }
@Override @Override
public E set(final int index, final E e) { public E set(final int index, @Nullable final E e) {
if (e == null) if (e == null)
throw new NullPointerException("Trying to set a null key"); throw new NullPointerException("Trying to set a null key");
return super.set(index, e); return super.set(index, e);

View File

@ -6,7 +6,7 @@
package com.wireguard.android.util; package com.wireguard.android.util;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import com.wireguard.util.Keyed; import com.wireguard.util.Keyed;
import com.wireguard.util.SortedKeyedList; import com.wireguard.util.SortedKeyedList;
@ -29,6 +29,7 @@ import java.util.Spliterator;
public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>> public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
extends ObservableKeyedArrayList<K, E> implements ObservableSortedKeyedList<K, E> { extends ObservableKeyedArrayList<K, E> implements ObservableSortedKeyedList<K, E> {
@Nullable
private final Comparator<? super K> comparator; private final Comparator<? super K> comparator;
private final transient KeyList<K, E> keyList = new KeyList<>(this); private final transient KeyList<K, E> keyList = new KeyList<>(this);
@ -75,7 +76,7 @@ public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
} }
@Override @Override
public boolean addAll(@NonNull final Collection<? extends E> c) { public boolean addAll(final Collection<? extends E> c) {
boolean didChange = false; boolean didChange = false;
for (final E e : c) for (final E e : c)
if (add(e)) if (add(e))
@ -84,12 +85,13 @@ public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
} }
@Override @Override
public boolean addAll(int index, @NonNull final Collection<? extends E> c) { public boolean addAll(int index, final Collection<? extends E> c) {
for (final E e : c) for (final E e : c)
add(index++, e); add(index++, e);
return true; return true;
} }
@Nullable
@Override @Override
public Comparator<? super K> comparator() { public Comparator<? super K> comparator() {
return comparator; return comparator;
@ -128,7 +130,6 @@ public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
} }
@Override @Override
@NonNull
public Set<K> keySet() { public Set<K> keySet() {
return keyList; return keyList;
} }
@ -168,7 +169,6 @@ public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
} }
@Override @Override
@NonNull
public Collection<E> values() { public Collection<E> values() {
return this; return this;
} }

View File

@ -7,6 +7,7 @@
package com.wireguard.android.util; package com.wireguard.android.util;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.wireguard.android.R; import com.wireguard.android.R;
@ -34,10 +35,10 @@ public class RootShell {
private final File localTemporaryDir; private final File localTemporaryDir;
private final Object lock = new Object(); private final Object lock = new Object();
private final String preamble; private final String preamble;
private Process process; @Nullable private Process process;
private BufferedReader stderr; @Nullable private BufferedReader stderr;
private OutputStreamWriter stdin; @Nullable private OutputStreamWriter stdin;
private BufferedReader stdout; @Nullable private BufferedReader stdout;
public RootShell(final Context context) { public RootShell(final Context context) {
deviceNotRootedMessage = context.getString(R.string.error_root); deviceNotRootedMessage = context.getString(R.string.error_root);
@ -80,7 +81,7 @@ public class RootShell {
* @param command Command to run as root. * @param command Command to run as root.
* @return The exit value of the command. * @return The exit value of the command.
*/ */
public int run(final Collection<String> output, final String command) public int run(@Nullable final Collection<String> output, final String command)
throws IOException, NoRootException { throws IOException, NoRootException {
synchronized (lock) { synchronized (lock) {
/* Start inside synchronized block to prevent a concurrent call to stop(). */ /* Start inside synchronized block to prevent a concurrent call to stop(). */

View File

@ -19,8 +19,7 @@ import java.util.zip.ZipFile;
public final class SharedLibraryLoader { public final class SharedLibraryLoader {
private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName(); private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName();
private SharedLibraryLoader() { private SharedLibraryLoader() { }
}
public static void loadSharedLibrary(final Context context, final String libName) { public static void loadSharedLibrary(final Context context, final String libName) {
Throwable noAbiException; Throwable noAbiException;

View File

@ -7,6 +7,7 @@
package com.wireguard.android.util; package com.wireguard.android.util;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable;
import android.system.OsConstants; import android.system.OsConstants;
import android.util.Log; import android.util.Log;
@ -39,20 +40,21 @@ public final class ToolsInstaller {
new File("/system/xbin"), new File("/system/xbin"),
new File("/system/bin"), new File("/system/bin"),
}; };
private static final File INSTALL_DIR = getInstallDir(); @Nullable private static final File INSTALL_DIR = getInstallDir();
private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName(); private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
private final File localBinaryDir; private final File localBinaryDir;
private final Object lock = new Object(); private final Object lock = new Object();
private final File nativeLibraryDir; private final File nativeLibraryDir;
private Boolean areToolsAvailable; @Nullable private Boolean areToolsAvailable;
private Boolean installAsMagiskModule; @Nullable private Boolean installAsMagiskModule;
public ToolsInstaller(final Context context) { public ToolsInstaller(final Context context) {
localBinaryDir = new File(context.getCacheDir(), "bin"); localBinaryDir = new File(context.getCacheDir(), "bin");
nativeLibraryDir = new File(context.getApplicationInfo().nativeLibraryDir); nativeLibraryDir = new File(context.getApplicationInfo().nativeLibraryDir);
} }
@Nullable
private static File getInstallDir() { private static File getInstallDir() {
final String path = System.getenv("PATH"); final String path = System.getenv("PATH");
if (path == null) if (path == null)

View File

@ -6,6 +6,7 @@
package com.wireguard.android.widget; package com.wireguard.android.widget;
import android.support.annotation.Nullable;
import android.text.InputFilter; import android.text.InputFilter;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
@ -25,7 +26,7 @@ public class KeyInputFilter implements InputFilter {
return new KeyInputFilter(); return new KeyInputFilter();
} }
@Override @Override @Nullable
public CharSequence filter(final CharSequence source, public CharSequence filter(final CharSequence source,
final int sStart, final int sEnd, final int sStart, final int sEnd,
final Spanned dest, final Spanned dest,

View File

@ -6,6 +6,7 @@
package com.wireguard.android.widget; package com.wireguard.android.widget;
import android.support.annotation.Nullable;
import android.text.InputFilter; import android.text.InputFilter;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
@ -25,7 +26,7 @@ public class NameInputFilter implements InputFilter {
return new NameInputFilter(); return new NameInputFilter();
} }
@Override @Override @Nullable
public CharSequence filter(final CharSequence source, public CharSequence filter(final CharSequence source,
final int sStart, final int sEnd, final int sStart, final int sEnd,
final Spanned dest, final Spanned dest,

View File

@ -25,11 +25,9 @@ import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.IntRange; import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.FloatProperty; import android.util.FloatProperty;
@TargetApi(Build.VERSION_CODES.N) @TargetApi(Build.VERSION_CODES.N)
public class SlashDrawable extends Drawable { public class SlashDrawable extends Drawable {
@ -53,26 +51,24 @@ public class SlashDrawable extends Drawable {
// Draw the slash washington-monument style; rotate to no-u-turn style // Draw the slash washington-monument style; rotate to no-u-turn style
private static final float DEFAULT_ROTATION = -45f; private static final float DEFAULT_ROTATION = -45f;
private Drawable mDrawable; private final Drawable mDrawable;
private final RectF mSlashRect = new RectF(0, 0, 0, 0); private final RectF mSlashRect = new RectF(0, 0, 0, 0);
private float mRotation; private float mRotation;
private boolean mSlashed; private boolean mSlashed;
private Mode mTintMode;
private ColorStateList mTintList;
private boolean mAnimationEnabled = true; private boolean mAnimationEnabled = true;
public SlashDrawable(final Drawable d) { public SlashDrawable(final Drawable d) {
setDrawable(d); mDrawable = d;
} }
@Override @Override
public int getIntrinsicHeight() { public int getIntrinsicHeight() {
return mDrawable != null ? mDrawable.getIntrinsicHeight(): 0; return mDrawable.getIntrinsicHeight();
} }
@Override @Override
public int getIntrinsicWidth() { public int getIntrinsicWidth() {
return mDrawable != null ? mDrawable.getIntrinsicWidth(): 0; return mDrawable.getIntrinsicWidth();
} }
@Override @Override
@ -81,17 +77,6 @@ public class SlashDrawable extends Drawable {
mDrawable.setBounds(bounds); mDrawable.setBounds(bounds);
} }
public void setDrawable(final Drawable d) {
mDrawable = d;
mDrawable.setCallback(getCallback());
mDrawable.setBounds(getBounds());
if (mTintMode != null)
mDrawable.setTintMode(mTintMode);
if (mTintList != null)
mDrawable.setTintList(mTintList);
invalidateSelf();
}
public void setRotation(final float rotation) { public void setRotation(final float rotation) {
if (mRotation == rotation) if (mRotation == rotation)
return; return;
@ -139,7 +124,7 @@ public class SlashDrawable extends Drawable {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void draw(@NonNull final Canvas canvas) { public void draw(final Canvas canvas) {
canvas.save(); canvas.save();
final Matrix m = new Matrix(); final Matrix m = new Matrix();
final int width = getBounds().width(); final int width = getBounds().width();
@ -201,7 +186,6 @@ public class SlashDrawable extends Drawable {
@Override @Override
public void setTintList(@Nullable final ColorStateList tint) { public void setTintList(@Nullable final ColorStateList tint) {
mTintList = tint;
super.setTintList(tint); super.setTintList(tint);
setDrawableTintList(tint); setDrawableTintList(tint);
mPaint.setColor(tint == null ? 0 : tint.getDefaultColor()); mPaint.setColor(tint == null ? 0 : tint.getDefaultColor());
@ -213,8 +197,7 @@ public class SlashDrawable extends Drawable {
} }
@Override @Override
public void setTintMode(@NonNull final Mode tintMode) { public void setTintMode(final Mode tintMode) {
mTintMode = tintMode;
super.setTintMode(tintMode); super.setTintMode(tintMode);
mDrawable.setTintMode(tintMode); mDrawable.setTintMode(tintMode);
} }

View File

@ -7,22 +7,23 @@ package com.wireguard.android.widget;
import android.content.Context; import android.content.Context;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.Switch; import android.widget.Switch;
public class ToggleSwitch extends Switch { public class ToggleSwitch extends Switch {
private boolean isRestoringState; private boolean isRestoringState;
private OnBeforeCheckedChangeListener listener; @Nullable private OnBeforeCheckedChangeListener listener;
@SuppressWarnings({"SameParameterValue", "WeakerAccess"})
public ToggleSwitch(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public ToggleSwitch(final Context context) { public ToggleSwitch(final Context context) {
this(context, null); this(context, null);
} }
@SuppressWarnings({"SameParameterValue", "WeakerAccess"})
public ToggleSwitch(final Context context, @Nullable final AttributeSet attrs) {
super(context, attrs);
}
@Override @Override
public void onRestoreInstanceState(final Parcelable state) { public void onRestoreInstanceState(final Parcelable state) {
isRestoringState = true; isRestoringState = true;

View File

@ -20,7 +20,7 @@ import android.graphics.drawable.LayerDrawable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Keep; import android.support.annotation.Keep;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.res.ResourcesCompat; import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.AppCompatTextView; import android.support.v7.widget.AppCompatTextView;
@ -57,32 +57,32 @@ public class FloatingActionsMenu extends ViewGroup {
private boolean mExpanded; private boolean mExpanded;
private final AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); private final AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private final AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); private final AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private FloatingActionButton mAddButton; @Nullable private FloatingActionButton mAddButton;
private RotatingDrawable mRotatingDrawable; @Nullable private RotatingDrawable mRotatingDrawable;
private int mMaxButtonWidth; private int mMaxButtonWidth;
private int mMaxButtonHeight; private int mMaxButtonHeight;
private int mLabelsStyle; private int mLabelsStyle;
private int mLabelsPosition; private int mLabelsPosition;
private int mButtonsCount; private int mButtonsCount;
private TouchDelegateGroup mTouchDelegateGroup; @Nullable private TouchDelegateGroup mTouchDelegateGroup;
private OnFloatingActionsMenuUpdateListener mListener; @Nullable private OnFloatingActionsMenuUpdateListener mListener;
private final Rect touchArea = new Rect(0, 0, 0, 0); private final Rect touchArea = new Rect(0, 0, 0, 0);
public FloatingActionsMenu(final Context context) { public FloatingActionsMenu(final Context context) {
this(context, null); this(context, null);
} }
public FloatingActionsMenu(final Context context, final AttributeSet attrs) { public FloatingActionsMenu(final Context context, @Nullable final AttributeSet attrs) {
super(context, attrs); super(context, attrs);
init(context, attrs); init(context, attrs);
} }
public FloatingActionsMenu(final Context context, final AttributeSet attrs, final int defStyle) { public FloatingActionsMenu(final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
init(context, attrs); init(context, attrs);
} }
private void init(final Context context, final AttributeSet attributeSet) { private void init(final Context context, @Nullable final AttributeSet attributeSet) {
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing)); mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing));
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin); mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset); mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
@ -530,7 +530,7 @@ public class FloatingActionsMenu extends ViewGroup {
} }
@Override @Override
public void writeToParcel(@NonNull final Parcel out, final int flags) { public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags); super.writeToParcel(out, flags);
out.writeInt(mExpanded ? 1 : 0); out.writeInt(mExpanded ? 1 : 0);
} }

View File

@ -8,6 +8,7 @@ package com.wireguard.android.widget.fab;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.TextView; import android.widget.TextView;
@ -16,17 +17,17 @@ import com.wireguard.android.R;
public class LabeledFloatingActionButton extends FloatingActionButton { public class LabeledFloatingActionButton extends FloatingActionButton {
private final String title; @Nullable private final String title;
public LabeledFloatingActionButton(final Context context) { public LabeledFloatingActionButton(final Context context) {
this(context, null); this(context, null);
} }
public LabeledFloatingActionButton(final Context context, final AttributeSet attrs) { public LabeledFloatingActionButton(final Context context, @Nullable final AttributeSet attrs) {
this(context, attrs, 0); this(context, attrs, 0);
} }
public LabeledFloatingActionButton(final Context context, final AttributeSet attrs, final int defStyle) { public LabeledFloatingActionButton(final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
final TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.LabeledFloatingActionButton, 0, 0); final TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.LabeledFloatingActionButton, 0, 0);
@ -34,10 +35,12 @@ public class LabeledFloatingActionButton extends FloatingActionButton {
attr.recycle(); attr.recycle();
} }
@Nullable
TextView getLabelView() { TextView getLabelView() {
return (TextView) getTag(R.id.fab_label); return (TextView) getTag(R.id.fab_label);
} }
@Nullable
public String getTitle() { public String getTitle() {
return title; return title;
} }

View File

@ -7,7 +7,7 @@
package com.wireguard.android.widget.fab; package com.wireguard.android.widget.fab;
import android.graphics.Rect; import android.graphics.Rect;
import android.support.annotation.NonNull; import android.support.annotation.Nullable;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.TouchDelegate; import android.view.TouchDelegate;
import android.view.View; import android.view.View;
@ -18,14 +18,14 @@ import java.util.Collection;
public class TouchDelegateGroup extends TouchDelegate { public class TouchDelegateGroup extends TouchDelegate {
private static final Rect USELESS_HACKY_RECT = new Rect(); private static final Rect USELESS_HACKY_RECT = new Rect();
private final Collection<TouchDelegate> mTouchDelegates = new ArrayList<>(); private final Collection<TouchDelegate> mTouchDelegates = new ArrayList<>();
private TouchDelegate mCurrentTouchDelegate; @Nullable private TouchDelegate mCurrentTouchDelegate;
private boolean mEnabled; private boolean mEnabled;
public TouchDelegateGroup(final View uselessHackyView) { public TouchDelegateGroup(final View uselessHackyView) {
super(USELESS_HACKY_RECT, uselessHackyView); super(USELESS_HACKY_RECT, uselessHackyView);
} }
public void addTouchDelegate(@NonNull final TouchDelegate touchDelegate) { public void addTouchDelegate(final TouchDelegate touchDelegate) {
mTouchDelegates.add(touchDelegate); mTouchDelegates.add(touchDelegate);
} }
@ -42,7 +42,7 @@ public class TouchDelegateGroup extends TouchDelegate {
} }
@Override @Override
public boolean onTouchEvent(@NonNull final MotionEvent event) { public boolean onTouchEvent(final MotionEvent event) {
if (!mEnabled) if (!mEnabled)
return false; return false;

View File

@ -6,6 +6,7 @@
package com.wireguard.config; package com.wireguard.config;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import java.util.HashMap; import java.util.HashMap;
@ -59,13 +60,13 @@ public enum Attribute {
return KEY_MAP.get(SEPARATOR_PATTERN.split(line)[0].toLowerCase()); return KEY_MAP.get(SEPARATOR_PATTERN.split(line)[0].toLowerCase());
} }
public static String[] stringToList(final String string) { public static String[] stringToList(@Nullable final String string) {
if (TextUtils.isEmpty(string)) if (TextUtils.isEmpty(string))
return EMPTY_LIST; return EMPTY_LIST;
return LIST_SEPARATOR_PATTERN.split(string.trim()); return LIST_SEPARATOR_PATTERN.split(string.trim());
} }
public String composeWith(final Object value) { public String composeWith(@Nullable final Object value) {
return String.format("%s = %s%n", token, value); return String.format("%s = %s%n", token, value);
} }
@ -77,11 +78,13 @@ public enum Attribute {
return String.format("%s = %s%n", token, iterableToString(value)); return String.format("%s = %s%n", token, iterableToString(value));
} }
@Nullable
public String parse(final CharSequence line) { public String parse(final CharSequence line) {
final Matcher matcher = pattern.matcher(line); final Matcher matcher = pattern.matcher(line);
return matcher.matches() ? matcher.group(1) : null; return matcher.matches() ? matcher.group(1) : null;
} }
@Nullable
public String[] parseList(final CharSequence line) { public String[] parseList(final CharSequence line) {
final Matcher matcher = pattern.matcher(line); final Matcher matcher = pattern.matcher(line);
return matcher.matches() ? stringToList(matcher.group(1)) : null; return matcher.matches() ? stringToList(matcher.group(1)) : null;

View File

@ -12,6 +12,7 @@ import android.databinding.ObservableArrayList;
import android.databinding.ObservableList; import android.databinding.ObservableList;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.android.databinding.library.baseAdapters.BR; import com.android.databinding.library.baseAdapters.BR;
@ -96,13 +97,19 @@ public class Config {
return new Observable[size]; return new Observable[size];
} }
}; };
private String name; @Nullable private String name;
private Interface.Observable observableInterface; private final Interface.Observable observableInterface;
private ObservableList<Peer.Observable> observablePeers; private final ObservableList<Peer.Observable> observablePeers;
public Observable(final Config parent, final String name) { public Observable(@Nullable final Config parent, @Nullable final String name) {
this.name = name; this.name = name;
loadData(parent);
observableInterface = new Interface.Observable(parent == null ? null : parent.interfaceSection);
observablePeers = new ObservableArrayList<>();
if (parent != null) {
for (final Peer peer : parent.getPeers())
observablePeers.add(new Peer.Observable(peer));
}
} }
private Observable(final Parcel in) { private Observable(final Parcel in) {
@ -144,15 +151,6 @@ public class Config {
return observablePeers; return observablePeers;
} }
protected void loadData(final Config parent) {
observableInterface = new Interface.Observable(parent == null ? null : parent.interfaceSection);
observablePeers = new ObservableArrayList<>();
if (parent != null) {
for (final Peer peer : parent.getPeers())
observablePeers.add(new Peer.Observable(peer));
}
}
public void setName(final String name) { public void setName(final String name) {
this.name = name; this.name = name;
notifyPropertyChanged(BR.name); notifyPropertyChanged(BR.name);

View File

@ -6,7 +6,6 @@
package com.wireguard.config; package com.wireguard.config;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -29,7 +28,6 @@ public final class InetAddresses {
// Prevent instantiation. // Prevent instantiation.
} }
@NonNull
public static InetAddress parse(@Nullable final String address) { public static InetAddress parse(@Nullable final String address) {
if (address == null || address.isEmpty()) if (address == null || address.isEmpty())
throw new IllegalArgumentException("Empty address"); throw new IllegalArgumentException("Empty address");

View File

@ -6,8 +6,6 @@
package com.wireguard.config; package com.wireguard.config;
import android.support.annotation.NonNull;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Objects; import java.util.Objects;
@ -16,7 +14,7 @@ public class InetNetwork {
private final InetAddress address; private final InetAddress address;
private final int mask; private final int mask;
public InetNetwork(@NonNull final String input) { public InetNetwork(final String input) {
final int slash = input.lastIndexOf('/'); final int slash = input.lastIndexOf('/');
final int rawMask; final int rawMask;
final String rawAddress; final String rawAddress;
@ -40,7 +38,6 @@ public class InetNetwork {
return Objects.equals(address, other.address) && mask == other.mask; return Objects.equals(address, other.address) && mask == other.mask;
} }
@NonNull
public InetAddress getAddress() { public InetAddress getAddress() {
return address; return address;
} }

View File

@ -10,6 +10,7 @@ import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.wireguard.android.BR; import com.wireguard.android.BR;
import com.wireguard.crypto.Keypair; import com.wireguard.crypto.Keypair;
@ -27,7 +28,7 @@ public class Interface {
private final List<InetNetwork> addressList; private final List<InetNetwork> addressList;
private final List<InetAddress> dnsList; private final List<InetAddress> dnsList;
private final List<String> excludedApplications; private final List<String> excludedApplications;
private Keypair keypair; @Nullable private Keypair keypair;
private int listenPort; private int listenPort;
private int mtu; private int mtu;
@ -37,7 +38,7 @@ public class Interface {
excludedApplications = new ArrayList<>(); excludedApplications = new ArrayList<>();
} }
private void addAddresses(final String[] addresses) { private void addAddresses(@Nullable final String[] addresses) {
if (addresses != null && addresses.length > 0) { if (addresses != null && addresses.length > 0) {
for (final String addr : addresses) { for (final String addr : addresses) {
if (addr.isEmpty()) if (addr.isEmpty())
@ -47,7 +48,7 @@ public class Interface {
} }
} }
private void addDnses(final String[] dnses) { private void addDnses(@Nullable final String[] dnses) {
if (dnses != null && dnses.length > 0) { if (dnses != null && dnses.length > 0) {
for (final String dns : dnses) { for (final String dns : dnses) {
dnsList.add(InetAddresses.parse(dns)); dnsList.add(InetAddresses.parse(dns));
@ -55,12 +56,13 @@ public class Interface {
} }
} }
private void addExcludedApplications(final String[] applications) { private void addExcludedApplications(@Nullable final String[] applications) {
if (applications != null && applications.length > 0) { if (applications != null && applications.length > 0) {
excludedApplications.addAll(Arrays.asList(applications)); excludedApplications.addAll(Arrays.asList(applications));
} }
} }
@Nullable
private String getAddressString() { private String getAddressString() {
if (addressList.isEmpty()) if (addressList.isEmpty())
return null; return null;
@ -71,6 +73,7 @@ public class Interface {
return addressList.toArray(new InetNetwork[addressList.size()]); return addressList.toArray(new InetNetwork[addressList.size()]);
} }
@Nullable
private String getDnsString() { private String getDnsString() {
if (dnsList.isEmpty()) if (dnsList.isEmpty())
return null; return null;
@ -88,6 +91,7 @@ public class Interface {
return dnsList.toArray(new InetAddress[dnsList.size()]); return dnsList.toArray(new InetAddress[dnsList.size()]);
} }
@Nullable
private String getExcludedApplicationsString() { private String getExcludedApplicationsString() {
if (excludedApplications.isEmpty()) if (excludedApplications.isEmpty())
return null; return null;
@ -102,6 +106,7 @@ public class Interface {
return listenPort; return listenPort;
} }
@Nullable
private String getListenPortString() { private String getListenPortString() {
if (listenPort == 0) if (listenPort == 0)
return null; return null;
@ -112,18 +117,21 @@ public class Interface {
return mtu; return mtu;
} }
@Nullable
private String getMtuString() { private String getMtuString() {
if (mtu == 0) if (mtu == 0)
return null; return null;
return Integer.toString(mtu); return Integer.toString(mtu);
} }
@Nullable
public String getPrivateKey() { public String getPrivateKey() {
if (keypair == null) if (keypair == null)
return null; return null;
return keypair.getPrivateKey(); return keypair.getPrivateKey();
} }
@Nullable
public String getPublicKey() { public String getPublicKey() {
if (keypair == null) if (keypair == null)
return null; return null;
@ -156,17 +164,17 @@ public class Interface {
} }
} }
private void setAddressString(final String addressString) { private void setAddressString(@Nullable final String addressString) {
addressList.clear(); addressList.clear();
addAddresses(Attribute.stringToList(addressString)); addAddresses(Attribute.stringToList(addressString));
} }
private void setDnsString(final String dnsString) { private void setDnsString(@Nullable final String dnsString) {
dnsList.clear(); dnsList.clear();
addDnses(Attribute.stringToList(dnsString)); addDnses(Attribute.stringToList(dnsString));
} }
private void setExcludedApplicationsString(final String applicationsString) { private void setExcludedApplicationsString(@Nullable final String applicationsString) {
excludedApplications.clear(); excludedApplications.clear();
addExcludedApplications(Attribute.stringToList(applicationsString)); addExcludedApplications(Attribute.stringToList(applicationsString));
} }
@ -175,7 +183,7 @@ public class Interface {
this.listenPort = listenPort; this.listenPort = listenPort;
} }
private void setListenPortString(final String port) { private void setListenPortString(@Nullable final String port) {
if (port != null && !port.isEmpty()) if (port != null && !port.isEmpty())
setListenPort(Integer.parseInt(port, 10)); setListenPort(Integer.parseInt(port, 10));
else else
@ -186,14 +194,14 @@ public class Interface {
this.mtu = mtu; this.mtu = mtu;
} }
private void setMtuString(final String mtu) { private void setMtuString(@Nullable final String mtu) {
if (mtu != null && !mtu.isEmpty()) if (mtu != null && !mtu.isEmpty())
setMtu(Integer.parseInt(mtu, 10)); setMtu(Integer.parseInt(mtu, 10));
else else
setMtu(0); setMtu(0);
} }
private void setPrivateKey(String privateKey) { private void setPrivateKey(@Nullable String privateKey) {
if (privateKey != null && privateKey.isEmpty()) if (privateKey != null && privateKey.isEmpty())
privateKey = null; privateKey = null;
keypair = privateKey == null ? null : new Keypair(privateKey); keypair = privateKey == null ? null : new Keypair(privateKey);
@ -229,15 +237,15 @@ public class Interface {
return new Observable[size]; return new Observable[size];
} }
}; };
private String addresses; @Nullable private String addresses;
private String dnses; @Nullable private String dnses;
private String excludedApplications; @Nullable private String excludedApplications;
private String listenPort; @Nullable private String listenPort;
private String mtu; @Nullable private String mtu;
private String privateKey; @Nullable private String privateKey;
private String publicKey; @Nullable private String publicKey;
public Observable(final Interface parent) { public Observable(@Nullable final Interface parent) {
if (parent != null) if (parent != null)
loadData(parent); loadData(parent);
} }
@ -276,16 +284,19 @@ public class Interface {
notifyPropertyChanged(BR.publicKey); notifyPropertyChanged(BR.publicKey);
} }
@Nullable
@Bindable @Bindable
public String getAddresses() { public String getAddresses() {
return addresses; return addresses;
} }
@Nullable
@Bindable @Bindable
public String getDnses() { public String getDnses() {
return dnses; return dnses;
} }
@Nullable
@Bindable @Bindable
public String getExcludedApplications() { public String getExcludedApplications() {
return excludedApplications; return excludedApplications;
@ -296,21 +307,25 @@ public class Interface {
return Attribute.stringToList(excludedApplications).length; return Attribute.stringToList(excludedApplications).length;
} }
@Nullable
@Bindable @Bindable
public String getListenPort() { public String getListenPort() {
return listenPort; return listenPort;
} }
@Nullable
@Bindable @Bindable
public String getMtu() { public String getMtu() {
return mtu; return mtu;
} }
@Nullable
@Bindable @Bindable
public String getPrivateKey() { public String getPrivateKey() {
return privateKey; return privateKey;
} }
@Nullable
@Bindable @Bindable
public String getPublicKey() { public String getPublicKey() {
return publicKey; return publicKey;

View File

@ -10,6 +10,7 @@ import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.android.databinding.library.baseAdapters.BR; import com.android.databinding.library.baseAdapters.BR;
import com.wireguard.crypto.KeyEncoding; import com.wireguard.crypto.KeyEncoding;
@ -34,16 +35,16 @@ import java9.lang.Iterables;
public class Peer { public class Peer {
private final List<InetNetwork> allowedIPsList; private final List<InetNetwork> allowedIPsList;
private InetSocketAddress endpoint; @Nullable private InetSocketAddress endpoint;
private int persistentKeepalive; private int persistentKeepalive;
private String preSharedKey; @Nullable private String preSharedKey;
private String publicKey; @Nullable private String publicKey;
public Peer() { public Peer() {
allowedIPsList = new ArrayList<>(); allowedIPsList = new ArrayList<>();
} }
private void addAllowedIPs(final String[] allowedIPs) { private void addAllowedIPs(@Nullable final String[] allowedIPs) {
if (allowedIPs != null && allowedIPs.length > 0) { if (allowedIPs != null && allowedIPs.length > 0) {
for (final String allowedIP : allowedIPs) { for (final String allowedIP : allowedIPs) {
allowedIPsList.add(new InetNetwork(allowedIP)); allowedIPsList.add(new InetNetwork(allowedIP));
@ -55,16 +56,19 @@ public class Peer {
return allowedIPsList.toArray(new InetNetwork[allowedIPsList.size()]); return allowedIPsList.toArray(new InetNetwork[allowedIPsList.size()]);
} }
@Nullable
private String getAllowedIPsString() { private String getAllowedIPsString() {
if (allowedIPsList.isEmpty()) if (allowedIPsList.isEmpty())
return null; return null;
return Attribute.iterableToString(allowedIPsList); return Attribute.iterableToString(allowedIPsList);
} }
@Nullable
public InetSocketAddress getEndpoint() { public InetSocketAddress getEndpoint() {
return endpoint; return endpoint;
} }
@Nullable
private String getEndpointString() { private String getEndpointString() {
if (endpoint == null) if (endpoint == null)
return null; return null;
@ -75,16 +79,19 @@ public class Peer {
return persistentKeepalive; return persistentKeepalive;
} }
@Nullable
private String getPersistentKeepaliveString() { private String getPersistentKeepaliveString() {
if (persistentKeepalive == 0) if (persistentKeepalive == 0)
return null; return null;
return Integer.valueOf(persistentKeepalive).toString(); return Integer.valueOf(persistentKeepalive).toString();
} }
@Nullable
public String getPreSharedKey() { public String getPreSharedKey() {
return preSharedKey; return preSharedKey;
} }
@Nullable
public String getPublicKey() { public String getPublicKey() {
return publicKey; return publicKey;
} }
@ -130,16 +137,16 @@ public class Peer {
} }
} }
private void setAllowedIPsString(final String allowedIPsString) { private void setAllowedIPsString(@Nullable final String allowedIPsString) {
allowedIPsList.clear(); allowedIPsList.clear();
addAllowedIPs(Attribute.stringToList(allowedIPsString)); addAllowedIPs(Attribute.stringToList(allowedIPsString));
} }
private void setEndpoint(final InetSocketAddress endpoint) { private void setEndpoint(@Nullable final InetSocketAddress endpoint) {
this.endpoint = endpoint; this.endpoint = endpoint;
} }
private void setEndpointString(final String endpoint) { private void setEndpointString(@Nullable final String endpoint) {
if (endpoint != null && !endpoint.isEmpty()) { if (endpoint != null && !endpoint.isEmpty()) {
final InetSocketAddress constructedEndpoint; final InetSocketAddress constructedEndpoint;
if (endpoint.indexOf('/') != -1 || endpoint.indexOf('?') != -1 || endpoint.indexOf('#') != -1) if (endpoint.indexOf('/') != -1 || endpoint.indexOf('?') != -1 || endpoint.indexOf('#') != -1)
@ -160,14 +167,14 @@ public class Peer {
this.persistentKeepalive = persistentKeepalive; this.persistentKeepalive = persistentKeepalive;
} }
private void setPersistentKeepaliveString(final String persistentKeepalive) { private void setPersistentKeepaliveString(@Nullable final String persistentKeepalive) {
if (persistentKeepalive != null && !persistentKeepalive.isEmpty()) if (persistentKeepalive != null && !persistentKeepalive.isEmpty())
setPersistentKeepalive(Integer.parseInt(persistentKeepalive, 10)); setPersistentKeepalive(Integer.parseInt(persistentKeepalive, 10));
else else
setPersistentKeepalive(0); setPersistentKeepalive(0);
} }
private void setPreSharedKey(String preSharedKey) { private void setPreSharedKey(@Nullable String preSharedKey) {
if (preSharedKey != null && preSharedKey.isEmpty()) if (preSharedKey != null && preSharedKey.isEmpty())
preSharedKey = null; preSharedKey = null;
if (preSharedKey != null) if (preSharedKey != null)
@ -175,7 +182,7 @@ public class Peer {
this.preSharedKey = preSharedKey; this.preSharedKey = preSharedKey;
} }
private void setPublicKey(String publicKey) { private void setPublicKey(@Nullable String publicKey) {
if (publicKey != null && publicKey.isEmpty()) if (publicKey != null && publicKey.isEmpty())
publicKey = null; publicKey = null;
if (publicKey != null) if (publicKey != null)
@ -211,12 +218,12 @@ public class Peer {
return new Observable[size]; return new Observable[size];
} }
}; };
private String allowedIPs; @Nullable private String allowedIPs;
private String endpoint; @Nullable private String endpoint;
private String persistentKeepalive; @Nullable private String persistentKeepalive;
private String preSharedKey; @Nullable private String preSharedKey;
private String publicKey; @Nullable private String publicKey;
private List<String> interfaceDNSRoutes; private final List<String> interfaceDNSRoutes = new ArrayList<>();
private int numSiblings; private int numSiblings;
public Observable(final Peer parent) { public Observable(final Peer parent) {
@ -230,7 +237,6 @@ public class Peer {
preSharedKey = in.readString(); preSharedKey = in.readString();
publicKey = in.readString(); publicKey = in.readString();
numSiblings = in.readInt(); numSiblings = in.readInt();
interfaceDNSRoutes = new ArrayList<>();
in.readStringList(interfaceDNSRoutes); in.readStringList(interfaceDNSRoutes);
} }
@ -284,27 +290,27 @@ public class Peer {
return numSiblings == 0 && Arrays.asList(Attribute.stringToList(allowedIPs)).containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4); return numSiblings == 0 && Arrays.asList(Attribute.stringToList(allowedIPs)).containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
} }
@Bindable @Bindable @Nullable
public String getAllowedIPs() { public String getAllowedIPs() {
return allowedIPs; return allowedIPs;
} }
@Bindable @Bindable @Nullable
public String getEndpoint() { public String getEndpoint() {
return endpoint; return endpoint;
} }
@Bindable @Bindable @Nullable
public String getPersistentKeepalive() { public String getPersistentKeepalive() {
return persistentKeepalive; return persistentKeepalive;
} }
@Bindable @Bindable @Nullable
public String getPreSharedKey() { public String getPreSharedKey() {
return preSharedKey; return preSharedKey;
} }
@Bindable @Bindable @Nullable
public String getPublicKey() { public String getPublicKey() {
return publicKey; return publicKey;
} }
@ -315,7 +321,6 @@ public class Peer {
persistentKeepalive = parent.getPersistentKeepaliveString(); persistentKeepalive = parent.getPersistentKeepaliveString();
preSharedKey = parent.getPreSharedKey(); preSharedKey = parent.getPreSharedKey();
publicKey = parent.getPublicKey(); publicKey = parent.getPublicKey();
interfaceDNSRoutes = new ArrayList<>();
} }
public void setAllowedIPs(final String allowedIPs) { public void setAllowedIPs(final String allowedIPs) {

View File

@ -5,6 +5,8 @@
package com.wireguard.crypto; package com.wireguard.crypto;
import android.support.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -93,7 +95,7 @@ public final class Curve25519 {
* if the base point of the curve should be used. * if the base point of the curve should be used.
*/ */
public static void eval(final byte[] result, final int offset, public static void eval(final byte[] result, final int offset,
final byte[] privateKey, final byte[] publicKey) { final byte[] privateKey, @Nullable final byte[] publicKey) {
final Curve25519 state = new Curve25519(); final Curve25519 state = new Curve25519();
try { try {
// Unpack the public key value. If null, use 9 as the base point. // Unpack the public key value. If null, use 9 as the base point.

View File

@ -0,0 +1,25 @@
/*
* Copyright © 2018 Eric Kuck <eric@bluelinelabs.com>.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.util;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
/**
* This annotation can be applied to a package, class or method to indicate that all
* class fields and method parameters and return values in that element are nonnull
* by default unless overridden.
*/
@Documented
@Nonnull
@TypeQualifierDefault({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NonNullForAll { }