Auto-format the source directories
Blame Jason for writing Java in vim. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
		
							parent
							
								
									4e134772d7
								
							
						
					
					
						commit
						a264f7ab36
					
				@ -11,10 +11,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <permission
 | 
					    <permission
 | 
				
			||||||
        android:name="${applicationId}.permission.CONTROL_TUNNELS"
 | 
					        android:name="${applicationId}.permission.CONTROL_TUNNELS"
 | 
				
			||||||
        android:protectionLevel="dangerous"
 | 
					        android:description="@string/permission_description"
 | 
				
			||||||
        android:icon="@mipmap/ic_launcher"
 | 
					        android:icon="@mipmap/ic_launcher"
 | 
				
			||||||
        android:label="@string/permission_label"
 | 
					        android:label="@string/permission_label"
 | 
				
			||||||
        android:description="@string/permission_description" />
 | 
					        android:protectionLevel="dangerous" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
        android:name=".Application"
 | 
					        android:name=".Application"
 | 
				
			||||||
 | 
				
			|||||||
@ -53,70 +53,18 @@ import java9.util.concurrent.CompletableFuture;
 | 
				
			|||||||
        compress = true)
 | 
					        compress = true)
 | 
				
			||||||
public class Application extends android.app.Application {
 | 
					public class Application extends android.app.Application {
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
 | 
					    @SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
 | 
				
			||||||
 | 
					    private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
 | 
					    @SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
 | 
				
			||||||
 | 
					    @Nullable private Backend backend;
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private RootShell rootShell;
 | 
					    @SuppressWarnings("NullableProblems") private RootShell rootShell;
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
 | 
					    @SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
 | 
					    @SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
 | 
				
			||||||
    @SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
 | 
					    @SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
 | 
				
			||||||
    @Nullable private Backend backend;
 | 
					 | 
				
			||||||
    private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Application() {
 | 
					    public Application() {
 | 
				
			||||||
        weakSelf = new WeakReference<>(this);
 | 
					        weakSelf = new WeakReference<>(this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* The ACRA password can be trivially reverse engineered and is open source anyway,
 | 
					 | 
				
			||||||
     * so there's no point in trying to protect it. However, we do want to at least
 | 
					 | 
				
			||||||
     * prevent innocent self-builders from uploading stuff to our crash reporter. So, we
 | 
					 | 
				
			||||||
     * check the DN of the certs that signed the apk, without even bothering to try
 | 
					 | 
				
			||||||
     * validating that they're authentic. It's a good enough heuristic.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @SuppressWarnings("deprecation")
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private static String getInstallSource(final Context context) {
 | 
					 | 
				
			||||||
        if (BuildConfig.DEBUG)
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            final CertificateFactory cf = CertificateFactory.getInstance("X509");
 | 
					 | 
				
			||||||
            for (final Signature sig : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures) {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    for (final String category : ((X509Certificate) cf.generateCertificate(new ByteArrayInputStream(sig.toByteArray()))).getSubjectDN().getName().split(", *")) {
 | 
					 | 
				
			||||||
                        final String[] parts = category.split("=", 2);
 | 
					 | 
				
			||||||
                        if (!"O".equals(parts[0]))
 | 
					 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                        switch (parts[1]) {
 | 
					 | 
				
			||||||
                            case "Google Inc.":
 | 
					 | 
				
			||||||
                                return "Play Store";
 | 
					 | 
				
			||||||
                            case "fdroid.org":
 | 
					 | 
				
			||||||
                                return "F-Droid";
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } catch (final Exception ignored) { }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (final Exception ignored) { }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void attachBaseContext(final Context context) {
 | 
					 | 
				
			||||||
        super.attachBaseContext(context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (BuildConfig.MIN_SDK_VERSION > Build.VERSION.SDK_INT) {
 | 
					 | 
				
			||||||
            final Intent intent = new Intent(Intent.ACTION_MAIN);
 | 
					 | 
				
			||||||
            intent.addCategory(Intent.CATEGORY_HOME);
 | 
					 | 
				
			||||||
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
 | 
					 | 
				
			||||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
					 | 
				
			||||||
            startActivity(intent);
 | 
					 | 
				
			||||||
            System.exit(0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final String installSource = getInstallSource(context);
 | 
					 | 
				
			||||||
        if (installSource != null) {
 | 
					 | 
				
			||||||
            ACRA.init(this);
 | 
					 | 
				
			||||||
            ACRA.getErrorReporter().putCustomData("installSource", installSource);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static Application get() {
 | 
					    public static Application get() {
 | 
				
			||||||
        return weakSelf.get();
 | 
					        return weakSelf.get();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -149,6 +97,40 @@ public class Application extends android.app.Application {
 | 
				
			|||||||
        return get().futureBackend;
 | 
					        return get().futureBackend;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* The ACRA password can be trivially reverse engineered and is open source anyway,
 | 
				
			||||||
 | 
					     * so there's no point in trying to protect it. However, we do want to at least
 | 
				
			||||||
 | 
					     * prevent innocent self-builders from uploading stuff to our crash reporter. So, we
 | 
				
			||||||
 | 
					     * check the DN of the certs that signed the apk, without even bothering to try
 | 
				
			||||||
 | 
					     * validating that they're authentic. It's a good enough heuristic.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SuppressWarnings("deprecation")
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    private static String getInstallSource(final Context context) {
 | 
				
			||||||
 | 
					        if (BuildConfig.DEBUG)
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            final CertificateFactory cf = CertificateFactory.getInstance("X509");
 | 
				
			||||||
 | 
					            for (final Signature sig : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    for (final String category : ((X509Certificate) cf.generateCertificate(new ByteArrayInputStream(sig.toByteArray()))).getSubjectDN().getName().split(", *")) {
 | 
				
			||||||
 | 
					                        final String[] parts = category.split("=", 2);
 | 
				
			||||||
 | 
					                        if (!"O".equals(parts[0]))
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        switch (parts[1]) {
 | 
				
			||||||
 | 
					                            case "Google Inc.":
 | 
				
			||||||
 | 
					                                return "Play Store";
 | 
				
			||||||
 | 
					                            case "fdroid.org":
 | 
				
			||||||
 | 
					                                return "F-Droid";
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (final Exception ignored) {
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (final Exception ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static RootShell getRootShell() {
 | 
					    public static RootShell getRootShell() {
 | 
				
			||||||
        return get().rootShell;
 | 
					        return get().rootShell;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -165,6 +147,26 @@ public class Application extends android.app.Application {
 | 
				
			|||||||
        return get().tunnelManager;
 | 
					        return get().tunnelManager;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void attachBaseContext(final Context context) {
 | 
				
			||||||
 | 
					        super.attachBaseContext(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (BuildConfig.MIN_SDK_VERSION > Build.VERSION.SDK_INT) {
 | 
				
			||||||
 | 
					            final Intent intent = new Intent(Intent.ACTION_MAIN);
 | 
				
			||||||
 | 
					            intent.addCategory(Intent.CATEGORY_HOME);
 | 
				
			||||||
 | 
					            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
 | 
				
			||||||
 | 
					            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
				
			||||||
 | 
					            startActivity(intent);
 | 
				
			||||||
 | 
					            System.exit(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String installSource = getInstallSource(context);
 | 
				
			||||||
 | 
					        if (installSource != null) {
 | 
				
			||||||
 | 
					            ACRA.init(this);
 | 
				
			||||||
 | 
					            ACRA.getErrorReporter().putCustomData("installSource", installSource);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onCreate() {
 | 
					    public void onCreate() {
 | 
				
			||||||
        super.onCreate();
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
				
			|||||||
@ -40,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();
 | 
				
			||||||
    @Nullable private Tunnel tunnel;
 | 
					 | 
				
			||||||
    @Nullable private Icon iconOn;
 | 
					 | 
				
			||||||
    @Nullable private Icon iconOff;
 | 
					    @Nullable private Icon iconOff;
 | 
				
			||||||
 | 
					    @Nullable private Icon iconOn;
 | 
				
			||||||
 | 
					    @Nullable private Tunnel tunnel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* This works around an annoying unsolved frameworks bug some people are hitting. */
 | 
					    /* This works around an annoying unsolved frameworks bug some people are hitting. */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -57,6 +57,22 @@ public class QuickTileService extends TileService {
 | 
				
			|||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onClick() {
 | 
				
			||||||
 | 
					        if (tunnel != null) {
 | 
				
			||||||
 | 
					            final Tile tile = getQsTile();
 | 
				
			||||||
 | 
					            if (tile != null) {
 | 
				
			||||||
 | 
					                tile.setIcon(tile.getIcon() == iconOn ? iconOff : iconOn);
 | 
				
			||||||
 | 
					                tile.updateTile();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            tunnel.setState(State.TOGGLE).whenComplete(this::onToggleFinished);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            final Intent intent = new Intent(this, MainActivity.class);
 | 
				
			||||||
 | 
					            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
				
			||||||
 | 
					            startActivityAndCollapse(intent);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onCreate() {
 | 
					    public void onCreate() {
 | 
				
			||||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
 | 
					        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
 | 
				
			||||||
@ -79,22 +95,6 @@ public class QuickTileService extends TileService {
 | 
				
			|||||||
        iconOff = Icon.createWithBitmap(b);
 | 
					        iconOff = Icon.createWithBitmap(b);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onClick() {
 | 
					 | 
				
			||||||
        if (tunnel != null) {
 | 
					 | 
				
			||||||
            final Tile tile = getQsTile();
 | 
					 | 
				
			||||||
            if (tile != null) {
 | 
					 | 
				
			||||||
                tile.setIcon(tile.getIcon() == iconOn ? iconOff : iconOn);
 | 
					 | 
				
			||||||
                tile.updateTile();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            tunnel.setState(State.TOGGLE).whenComplete(this::onToggleFinished);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            final Intent intent = new Intent(this, MainActivity.class);
 | 
					 | 
				
			||||||
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
					 | 
				
			||||||
            startActivityAndCollapse(intent);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onStartListening() {
 | 
					    public void onStartListening() {
 | 
				
			||||||
        Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback);
 | 
					        Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback);
 | 
				
			||||||
 | 
				
			|||||||
@ -92,8 +92,8 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
 | 
				
			|||||||
        public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
 | 
					        public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
 | 
				
			||||||
            addPreferencesFromResource(R.xml.preferences);
 | 
					            addPreferencesFromResource(R.xml.preferences);
 | 
				
			||||||
            final Preference wgQuickOnlyPrefs[] = {
 | 
					            final Preference wgQuickOnlyPrefs[] = {
 | 
				
			||||||
                getPreferenceManager().findPreference("tools_installer"),
 | 
					                    getPreferenceManager().findPreference("tools_installer"),
 | 
				
			||||||
                getPreferenceManager().findPreference("restore_on_boot")
 | 
					                    getPreferenceManager().findPreference("restore_on_boot")
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            for (final Preference pref : wgQuickOnlyPrefs)
 | 
					            for (final Preference pref : wgQuickOnlyPrefs)
 | 
				
			||||||
                pref.setVisible(false);
 | 
					                pref.setVisible(false);
 | 
				
			||||||
 | 
				
			|||||||
@ -19,9 +19,9 @@ 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();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable private static Resources lastResources;
 | 
					 | 
				
			||||||
    private static boolean lastDarkMode;
 | 
					    private static boolean lastDarkMode;
 | 
				
			||||||
 | 
					    @Nullable private static Resources lastResources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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)
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@ -33,7 +33,8 @@ public abstract class ThemeChangeAwareActivity extends AppCompatActivity impleme
 | 
				
			|||||||
                f = o.getClass().getDeclaredField("mResourcesImpl");
 | 
					                f = o.getClass().getDeclaredField("mResourcesImpl");
 | 
				
			||||||
                f.setAccessible(true);
 | 
					                f.setAccessible(true);
 | 
				
			||||||
                o = f.get(o);
 | 
					                o = f.get(o);
 | 
				
			||||||
            } catch (final Exception ignored) { }
 | 
					            } catch (final Exception ignored) {
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            f = o.getClass().getDeclaredField("mDrawableCache");
 | 
					            f = o.getClass().getDeclaredField("mDrawableCache");
 | 
				
			||||||
            f.setAccessible(true);
 | 
					            f.setAccessible(true);
 | 
				
			||||||
            o = f.get(o);
 | 
					            o = f.get(o);
 | 
				
			||||||
 | 
				
			|||||||
@ -53,14 +53,11 @@ public interface Backend {
 | 
				
			|||||||
    Statistics getStatistics(Tunnel tunnel) throws Exception;
 | 
					    Statistics getStatistics(Tunnel tunnel) throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Set the state of a tunnel.
 | 
					     * Determine type name of underlying backend.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param tunnel The tunnel to control the state of.
 | 
					     * @return Type name
 | 
				
			||||||
     * @param state  The new state for this tunnel. Must be {@code UP}, {@code DOWN}, or
 | 
					 | 
				
			||||||
     *               {@code TOGGLE}.
 | 
					 | 
				
			||||||
     * @return The updated state of the tunnel.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    State setState(Tunnel tunnel, State state) throws Exception;
 | 
					    String getTypeName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Determine version of underlying backend.
 | 
					     * Determine version of underlying backend.
 | 
				
			||||||
@ -71,9 +68,12 @@ public interface Backend {
 | 
				
			|||||||
    String getVersion() throws Exception;
 | 
					    String getVersion() throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Determine type name of underlying backend.
 | 
					     * Set the state of a tunnel.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return Type name
 | 
					     * @param tunnel The tunnel to control the state of.
 | 
				
			||||||
 | 
					     * @param state  The new state for this tunnel. Must be {@code UP}, {@code DOWN}, or
 | 
				
			||||||
 | 
					     *               {@code TOGGLE}.
 | 
				
			||||||
 | 
					     * @return The updated state of the tunnel.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    String getTypeName();
 | 
					    State setState(Tunnel tunnel, State state) throws Exception;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -59,12 +59,6 @@ public final class GoBackend implements Backend {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static native String wgVersion();
 | 
					    private static native String wgVersion();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getVersion() { return wgVersion(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getTypeName() { return "Go userspace"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Config applyConfig(final Tunnel tunnel, final Config config) throws Exception {
 | 
					    public Config applyConfig(final Tunnel tunnel, final Config config) throws Exception {
 | 
				
			||||||
        if (tunnel.getState() == State.UP) {
 | 
					        if (tunnel.getState() == State.UP) {
 | 
				
			||||||
@ -101,6 +95,16 @@ public final class GoBackend implements Backend {
 | 
				
			|||||||
        return new Statistics();
 | 
					        return new Statistics();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getTypeName() {
 | 
				
			||||||
 | 
					        return "Go userspace";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getVersion() {
 | 
				
			||||||
 | 
					        return wgVersion();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public State setState(final Tunnel tunnel, State state) throws Exception {
 | 
					    public State setState(final Tunnel tunnel, State state) throws Exception {
 | 
				
			||||||
        final State originalState = getState(tunnel);
 | 
					        final State originalState = getState(tunnel);
 | 
				
			||||||
 | 
				
			|||||||
@ -40,18 +40,6 @@ public final class WgQuickBackend implements Backend {
 | 
				
			|||||||
        localTemporaryDir = new File(context.getCacheDir(), "tmp");
 | 
					        localTemporaryDir = new File(context.getCacheDir(), "tmp");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getVersion() throws Exception {
 | 
					 | 
				
			||||||
        final List<String> output = new ArrayList<>();
 | 
					 | 
				
			||||||
        if (Application.getRootShell()
 | 
					 | 
				
			||||||
                .run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
 | 
					 | 
				
			||||||
            throw new Exception("Unable to determine kernel module version");
 | 
					 | 
				
			||||||
        return output.get(0);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getTypeName() { return "Kernel module"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Config applyConfig(final Tunnel tunnel, final Config config) throws Exception {
 | 
					    public Config applyConfig(final Tunnel tunnel, final Config config) throws Exception {
 | 
				
			||||||
        if (tunnel.getState() == State.UP) {
 | 
					        if (tunnel.getState() == State.UP) {
 | 
				
			||||||
@ -94,6 +82,20 @@ public final class WgQuickBackend implements Backend {
 | 
				
			|||||||
        return new Statistics();
 | 
					        return new Statistics();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getTypeName() {
 | 
				
			||||||
 | 
					        return "Kernel module";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getVersion() throws Exception {
 | 
				
			||||||
 | 
					        final List<String> output = new ArrayList<>();
 | 
				
			||||||
 | 
					        if (Application.getRootShell()
 | 
				
			||||||
 | 
					                .run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
 | 
				
			||||||
 | 
					            throw new Exception("Unable to determine kernel module version");
 | 
				
			||||||
 | 
					        return output.get(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public State setState(final Tunnel tunnel, State state) throws Exception {
 | 
					    public State setState(final Tunnel tunnel, State state) throws Exception {
 | 
				
			||||||
        final State originalState = getState(tunnel);
 | 
					        final State originalState = getState(tunnel);
 | 
				
			||||||
 | 
				
			|||||||
@ -40,11 +40,6 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        setList(list);
 | 
					        setList(list);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int getItemCount() {
 | 
					 | 
				
			||||||
        return list != null ? list.size() : 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					    @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())
 | 
				
			||||||
@ -52,6 +47,11 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        return list.get(position);
 | 
					        return list.get(position);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getItemCount() {
 | 
				
			||||||
 | 
					        return list != null ? list.size() : 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public long getItemId(final int position) {
 | 
					    public long getItemId(final int position) {
 | 
				
			||||||
        final K key = getKey(position);
 | 
					        final K key = getKey(position);
 | 
				
			||||||
@ -64,11 +64,6 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        return item != null ? item.getKey() : null;
 | 
					        return item != null ? item.getKey() : null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
 | 
					 | 
				
			||||||
        return new ViewHolder(DataBindingUtil.inflate(layoutInflater, layoutId, parent, false));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("unchecked")
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onBindViewHolder(final ViewHolder holder, final int position) {
 | 
					    public void onBindViewHolder(final ViewHolder holder, final int position) {
 | 
				
			||||||
@ -85,6 +80,11 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
 | 
				
			||||||
 | 
					        return new ViewHolder(DataBindingUtil.inflate(layoutInflater, layoutId, parent, false));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void setList(@Nullable 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);
 | 
				
			||||||
@ -99,6 +99,10 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        this.rowConfigurationHandler = rowConfigurationHandler;
 | 
					        this.rowConfigurationHandler = rowConfigurationHandler;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface RowConfigurationHandler<B extends ViewDataBinding, T> {
 | 
				
			||||||
 | 
					        void onConfigureRow(B binding, T item, int position);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final class OnListChangedCallback<E extends Keyed<?>>
 | 
					    private static final class OnListChangedCallback<E extends Keyed<?>>
 | 
				
			||||||
            extends ObservableList.OnListChangedCallback<ObservableList<E>> {
 | 
					            extends ObservableList.OnListChangedCallback<ObservableList<E>> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,8 +156,4 @@ public class ObservableKeyedRecyclerViewAdapter<K, E extends Keyed<? extends K>>
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public interface RowConfigurationHandler<B extends ViewDataBinding, T> {
 | 
					 | 
				
			||||||
        void onConfigureRow(B binding, T item, int position);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,9 +34,8 @@ import java.util.List;
 | 
				
			|||||||
public class AppListDialogFragment extends DialogFragment {
 | 
					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 ObservableKeyedList<String, ApplicationData> appData = new ObservableKeyedArrayList<>();
 | 
					    private final ObservableKeyedList<String, ApplicationData> appData = new ObservableKeyedArrayList<>();
 | 
				
			||||||
 | 
					    private List<String> currentlyExcludedApps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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) {
 | 
				
			||||||
        final Bundle extras = new Bundle();
 | 
					        final Bundle extras = new Bundle();
 | 
				
			||||||
@ -47,39 +46,6 @@ 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 Dialog onCreateDialog(final Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
 | 
					 | 
				
			||||||
        alertDialogBuilder.setTitle(R.string.excluded_applications);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final AppListDialogFragmentBinding binding = AppListDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false);
 | 
					 | 
				
			||||||
        binding.executePendingBindings();
 | 
					 | 
				
			||||||
        alertDialogBuilder.setView(binding.getRoot());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        alertDialogBuilder.setPositiveButton(R.string.set_exclusions, (dialog, which) -> setExclusionsAndDismiss());
 | 
					 | 
				
			||||||
        alertDialogBuilder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
 | 
					 | 
				
			||||||
        alertDialogBuilder.setNeutralButton(R.string.deselect_all, (dialog, which) -> { });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        binding.setFragment(this);
 | 
					 | 
				
			||||||
        binding.setAppData(appData);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loadData();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final AlertDialog dialog = alertDialogBuilder.create();
 | 
					 | 
				
			||||||
        dialog.setOnShowListener(d -> dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(view -> {
 | 
					 | 
				
			||||||
                for (final ApplicationData app : appData)
 | 
					 | 
				
			||||||
                    app.setExcludedFromTunnel(false);
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
        return dialog;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void loadData() {
 | 
					    private void loadData() {
 | 
				
			||||||
        final Activity activity = getActivity();
 | 
					        final Activity activity = getActivity();
 | 
				
			||||||
        if (activity == null) {
 | 
					        if (activity == null) {
 | 
				
			||||||
@ -113,6 +79,40 @@ public class AppListDialogFragment extends DialogFragment {
 | 
				
			|||||||
        }));
 | 
					        }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        currentlyExcludedApps = Arrays.asList(getArguments().getStringArray(KEY_EXCLUDED_APPS));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Dialog onCreateDialog(final Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
 | 
				
			||||||
 | 
					        alertDialogBuilder.setTitle(R.string.excluded_applications);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final AppListDialogFragmentBinding binding = AppListDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false);
 | 
				
			||||||
 | 
					        binding.executePendingBindings();
 | 
				
			||||||
 | 
					        alertDialogBuilder.setView(binding.getRoot());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        alertDialogBuilder.setPositiveButton(R.string.set_exclusions, (dialog, which) -> setExclusionsAndDismiss());
 | 
				
			||||||
 | 
					        alertDialogBuilder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
 | 
				
			||||||
 | 
					        alertDialogBuilder.setNeutralButton(R.string.deselect_all, (dialog, which) -> {
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.setFragment(this);
 | 
				
			||||||
 | 
					        binding.setAppData(appData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loadData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final AlertDialog dialog = alertDialogBuilder.create();
 | 
				
			||||||
 | 
					        dialog.setOnShowListener(d -> dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(view -> {
 | 
				
			||||||
 | 
					            for (final ApplicationData app : appData)
 | 
				
			||||||
 | 
					                app.setExcludedFromTunnel(false);
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					        return dialog;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void setExclusionsAndDismiss() {
 | 
					    void setExclusionsAndDismiss() {
 | 
				
			||||||
        final List<String> excludedApps = new ArrayList<>();
 | 
					        final List<String> excludedApps = new ArrayList<>();
 | 
				
			||||||
        for (final ApplicationData data : appData) {
 | 
					        for (final ApplicationData data : appData) {
 | 
				
			||||||
 | 
				
			|||||||
@ -33,9 +33,8 @@ import com.wireguard.android.util.ExceptionLoggers;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class BaseFragment extends Fragment implements OnSelectedTunnelChangedListener {
 | 
					public abstract class BaseFragment extends Fragment implements OnSelectedTunnelChangedListener {
 | 
				
			||||||
    private static final String TAG = "WireGuard/" + BaseFragment.class.getSimpleName();
 | 
					 | 
				
			||||||
    private static final int REQUEST_CODE_VPN_PERMISSION = 23491;
 | 
					    private static final int REQUEST_CODE_VPN_PERMISSION = 23491;
 | 
				
			||||||
 | 
					    private static final String TAG = "WireGuard/" + BaseFragment.class.getSimpleName();
 | 
				
			||||||
    @Nullable private BaseActivity activity;
 | 
					    @Nullable private BaseActivity activity;
 | 
				
			||||||
    @Nullable private Tunnel pendingTunnel;
 | 
					    @Nullable private Tunnel pendingTunnel;
 | 
				
			||||||
    @Nullable private Boolean pendingTunnelUp;
 | 
					    @Nullable private Boolean pendingTunnelUp;
 | 
				
			||||||
@ -45,6 +44,18 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
 | 
				
			|||||||
        return activity != null ? activity.getSelectedTunnel() : null;
 | 
					        return activity != null ? activity.getSelectedTunnel() : null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
 | 
				
			||||||
 | 
					        super.onActivityResult(requestCode, resultCode, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (requestCode == REQUEST_CODE_VPN_PERMISSION) {
 | 
				
			||||||
 | 
					            if (pendingTunnel != null && pendingTunnelUp != null)
 | 
				
			||||||
 | 
					                setTunnelStateWithPermissionsResult(pendingTunnel, pendingTunnelUp);
 | 
				
			||||||
 | 
					            pendingTunnel = null;
 | 
				
			||||||
 | 
					            pendingTunnelUp = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onAttach(final Context context) {
 | 
					    public void onAttach(final Context context) {
 | 
				
			||||||
        super.onAttach(context);
 | 
					        super.onAttach(context);
 | 
				
			||||||
@ -64,18 +75,6 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
 | 
				
			|||||||
        super.onDetach();
 | 
					        super.onDetach();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
 | 
					 | 
				
			||||||
        super.onActivityResult(requestCode, resultCode, data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (requestCode == REQUEST_CODE_VPN_PERMISSION) {
 | 
					 | 
				
			||||||
            if (pendingTunnel != null && pendingTunnelUp != null)
 | 
					 | 
				
			||||||
                setTunnelStateWithPermissionsResult(pendingTunnel, pendingTunnelUp);
 | 
					 | 
				
			||||||
            pendingTunnel = null;
 | 
					 | 
				
			||||||
            pendingTunnelUp = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected void setSelectedTunnel(@Nullable final Tunnel tunnel) {
 | 
					    protected void setSelectedTunnel(@Nullable final Tunnel tunnel) {
 | 
				
			||||||
        if (activity != null)
 | 
					        if (activity != null)
 | 
				
			||||||
            activity.setSelectedTunnel(tunnel);
 | 
					            activity.setSelectedTunnel(tunnel);
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,6 @@ import android.os.Bundle;
 | 
				
			|||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
import android.support.v4.app.DialogFragment;
 | 
					import android.support.v4.app.DialogFragment;
 | 
				
			||||||
import android.support.v7.app.AlertDialog;
 | 
					import android.support.v7.app.AlertDialog;
 | 
				
			||||||
import android.view.WindowManager;
 | 
					 | 
				
			||||||
import android.view.inputmethod.InputMethodManager;
 | 
					import android.view.inputmethod.InputMethodManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.wireguard.android.Application;
 | 
					import com.wireguard.android.Application;
 | 
				
			||||||
@ -27,9 +26,8 @@ import java.util.Objects;
 | 
				
			|||||||
public class ConfigNamingDialogFragment extends DialogFragment {
 | 
					public class ConfigNamingDialogFragment extends DialogFragment {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final String KEY_CONFIG_TEXT = "config_text";
 | 
					    private static final String KEY_CONFIG_TEXT = "config_text";
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable private Config config;
 | 
					 | 
				
			||||||
    @Nullable private ConfigNamingDialogFragmentBinding binding;
 | 
					    @Nullable private ConfigNamingDialogFragmentBinding binding;
 | 
				
			||||||
 | 
					    @Nullable private Config config;
 | 
				
			||||||
    @Nullable private InputMethodManager imm;
 | 
					    @Nullable private InputMethodManager imm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static ConfigNamingDialogFragment newInstance(final String configText) {
 | 
					    public static ConfigNamingDialogFragment newInstance(final String configText) {
 | 
				
			||||||
@ -40,6 +38,26 @@ public class ConfigNamingDialogFragment extends DialogFragment {
 | 
				
			|||||||
        return fragment;
 | 
					        return fragment;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void createTunnelAndDismiss() {
 | 
				
			||||||
 | 
					        if (binding != null) {
 | 
				
			||||||
 | 
					            final String name = binding.tunnelNameText.getText().toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Application.getTunnelManager().create(name, config).whenComplete((tunnel, throwable) -> {
 | 
				
			||||||
 | 
					                if (tunnel != null) {
 | 
				
			||||||
 | 
					                    dismiss();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    binding.tunnelNameTextLayout.setError(throwable.getMessage());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void dismiss() {
 | 
				
			||||||
 | 
					        setKeyboardVisible(false);
 | 
				
			||||||
 | 
					        super.dismiss();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
					    public void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
@ -51,17 +69,6 @@ public class ConfigNamingDialogFragment extends DialogFragment {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override public void onResume() {
 | 
					 | 
				
			||||||
        super.onResume();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final AlertDialog dialog = (AlertDialog) getDialog();
 | 
					 | 
				
			||||||
        if (dialog != null) {
 | 
					 | 
				
			||||||
            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> createTunnelAndDismiss());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            setKeyboardVisible(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Dialog onCreateDialog(final Bundle savedInstanceState) {
 | 
					    public Dialog onCreateDialog(final Bundle savedInstanceState) {
 | 
				
			||||||
        final Activity activity = getActivity();
 | 
					        final Activity activity = getActivity();
 | 
				
			||||||
@ -81,23 +88,14 @@ public class ConfigNamingDialogFragment extends DialogFragment {
 | 
				
			|||||||
        return alertDialogBuilder.create();
 | 
					        return alertDialogBuilder.create();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override public void onResume() {
 | 
				
			||||||
    public void dismiss() {
 | 
					        super.onResume();
 | 
				
			||||||
        setKeyboardVisible(false);
 | 
					 | 
				
			||||||
        super.dismiss();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void createTunnelAndDismiss() {
 | 
					        final AlertDialog dialog = (AlertDialog) getDialog();
 | 
				
			||||||
        if (binding != null) {
 | 
					        if (dialog != null) {
 | 
				
			||||||
            final String name = binding.tunnelNameText.getText().toString();
 | 
					            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> createTunnelAndDismiss());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Application.getTunnelManager().create(name, config).whenComplete((tunnel, throwable) -> {
 | 
					            setKeyboardVisible(true);
 | 
				
			||||||
                if (tunnel != null) {
 | 
					 | 
				
			||||||
                    dismiss();
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    binding.tunnelNameTextLayout.setError(throwable.getMessage());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -48,8 +48,59 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
 | 
				
			|||||||
    private static final String KEY_LOCAL_CONFIG = "local_config";
 | 
					    private static final String KEY_LOCAL_CONFIG = "local_config";
 | 
				
			||||||
    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 final Collection<Object> breakObjectOrientedLayeringHandlerReceivers = new ArrayList<>();
 | 
				
			||||||
    @Nullable private TunnelEditorFragmentBinding binding;
 | 
					    @Nullable private TunnelEditorFragmentBinding binding;
 | 
				
			||||||
 | 
					    private final Observable.OnPropertyChangedCallback breakObjectOrientedLayeringHandler = new Observable.OnPropertyChangedCallback() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onPropertyChanged(final Observable sender, final int propertyId) {
 | 
				
			||||||
 | 
					            if (binding == null)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            final Config.Observable config = binding.getConfig();
 | 
				
			||||||
 | 
					            if (config == null)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            if (propertyId == BR.config) {
 | 
				
			||||||
 | 
					                config.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
				
			||||||
 | 
					                breakObjectOrientedLayeringHandlerReceivers.add(config);
 | 
				
			||||||
 | 
					                config.getInterfaceSection().addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
				
			||||||
 | 
					                breakObjectOrientedLayeringHandlerReceivers.add(config.getInterfaceSection());
 | 
				
			||||||
 | 
					                config.getPeers().addOnListChangedCallback(breakObjectListOrientedLayeringHandler);
 | 
				
			||||||
 | 
					                breakObjectOrientedLayeringHandlerReceivers.add(config.getPeers());
 | 
				
			||||||
 | 
					            } else if (propertyId == BR.dnses || propertyId == BR.peers)
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            final int numSiblings = config.getPeers().size() - 1;
 | 
				
			||||||
 | 
					            for (final Peer.Observable peer : config.getPeers()) {
 | 
				
			||||||
 | 
					                peer.setInterfaceDNSRoutes(config.getInterfaceSection().getDnses());
 | 
				
			||||||
 | 
					                peer.setNumSiblings(numSiblings);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    private final ObservableList.OnListChangedCallback<? extends ObservableList<Peer.Observable>> breakObjectListOrientedLayeringHandler = new ObservableList.OnListChangedCallback<ObservableList<Peer.Observable>>() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onChanged(final ObservableList<Peer.Observable> sender) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onItemRangeChanged(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onItemRangeInserted(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) {
 | 
				
			||||||
 | 
					            if (binding != null)
 | 
				
			||||||
 | 
					                breakObjectOrientedLayeringHandler.onPropertyChanged(binding.getConfig(), BR.peers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onItemRangeMoved(final ObservableList<Peer.Observable> sender, final int fromPosition, final int toPosition, final int itemCount) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onItemRangeRemoved(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) {
 | 
				
			||||||
 | 
					            if (binding != null)
 | 
				
			||||||
 | 
					                breakObjectOrientedLayeringHandler.onPropertyChanged(binding.getConfig(), BR.peers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    @Nullable 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) {
 | 
				
			||||||
@ -87,54 +138,6 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
 | 
				
			|||||||
        inflater.inflate(R.menu.config_editor, menu);
 | 
					        inflater.inflate(R.menu.config_editor, menu);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final ObservableList.OnListChangedCallback<? extends ObservableList<Peer.Observable>> breakObjectListOrientedLayeringHandler = new ObservableList.OnListChangedCallback<ObservableList<Peer.Observable>>() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onChanged(final ObservableList<Peer.Observable> sender) { }
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onItemRangeChanged(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) { }
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onItemRangeMoved(final ObservableList<Peer.Observable> sender, final int fromPosition, final int toPosition, final int itemCount) { }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onItemRangeInserted(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) {
 | 
					 | 
				
			||||||
            if (binding != null)
 | 
					 | 
				
			||||||
                breakObjectOrientedLayeringHandler.onPropertyChanged(binding.getConfig(), BR.peers);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onItemRangeRemoved(final ObservableList<Peer.Observable> sender, final int positionStart, final int itemCount) {
 | 
					 | 
				
			||||||
            if (binding != null)
 | 
					 | 
				
			||||||
                breakObjectOrientedLayeringHandler.onPropertyChanged(binding.getConfig(), BR.peers);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Collection<Object> breakObjectOrientedLayeringHandlerReceivers = new ArrayList<>();
 | 
					 | 
				
			||||||
    private final Observable.OnPropertyChangedCallback breakObjectOrientedLayeringHandler = new Observable.OnPropertyChangedCallback() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onPropertyChanged(final Observable sender, final int propertyId) {
 | 
					 | 
				
			||||||
            if (binding == null)
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            final Config.Observable config = binding.getConfig();
 | 
					 | 
				
			||||||
            if (config == null)
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            if (propertyId == BR.config) {
 | 
					 | 
				
			||||||
                config.addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
					 | 
				
			||||||
                breakObjectOrientedLayeringHandlerReceivers.add(config);
 | 
					 | 
				
			||||||
                config.getInterfaceSection().addOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
					 | 
				
			||||||
                breakObjectOrientedLayeringHandlerReceivers.add(config.getInterfaceSection());
 | 
					 | 
				
			||||||
                config.getPeers().addOnListChangedCallback(breakObjectListOrientedLayeringHandler);
 | 
					 | 
				
			||||||
                breakObjectOrientedLayeringHandlerReceivers.add(config.getPeers());
 | 
					 | 
				
			||||||
            } else if (propertyId == BR.dnses || propertyId == BR.peers)
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            final int numSiblings = config.getPeers().size() - 1;
 | 
					 | 
				
			||||||
            for (final Peer.Observable peer : config.getPeers()) {
 | 
					 | 
				
			||||||
                peer.setInterfaceDNSRoutes(config.getInterfaceSection().getDnses());
 | 
					 | 
				
			||||||
                peer.setNumSiblings(numSiblings);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
 | 
					    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
 | 
				
			||||||
                             @Nullable final Bundle savedInstanceState) {
 | 
					                             @Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
@ -152,13 +155,19 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
 | 
				
			|||||||
        binding = null;
 | 
					        binding = null;
 | 
				
			||||||
        for (final Object o : breakObjectOrientedLayeringHandlerReceivers) {
 | 
					        for (final Object o : breakObjectOrientedLayeringHandlerReceivers) {
 | 
				
			||||||
            if (o instanceof Observable)
 | 
					            if (o instanceof Observable)
 | 
				
			||||||
                ((Observable)o).removeOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
					                ((Observable) o).removeOnPropertyChangedCallback(breakObjectOrientedLayeringHandler);
 | 
				
			||||||
            else if (o instanceof ObservableList)
 | 
					            else if (o instanceof ObservableList)
 | 
				
			||||||
                ((ObservableList)o).removeOnListChangedCallback(breakObjectListOrientedLayeringHandler);
 | 
					                ((ObservableList) o).removeOnListChangedCallback(breakObjectListOrientedLayeringHandler);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        super.onDestroyView();
 | 
					        super.onDestroyView();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void onFinished() {
 | 
					    private void onFinished() {
 | 
				
			||||||
        // Hide the keyboard; it rarely goes away on its own.
 | 
					        // Hide the keyboard; it rarely goes away on its own.
 | 
				
			||||||
        final Activity activity = getActivity();
 | 
					        final Activity activity = getActivity();
 | 
				
			||||||
@ -217,6 +226,15 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onRequestSetExcludedApplications(@SuppressWarnings("unused") final View view) {
 | 
				
			||||||
 | 
					        final FragmentManager fragmentManager = getFragmentManager();
 | 
				
			||||||
 | 
					        if (fragmentManager != null && binding != null) {
 | 
				
			||||||
 | 
					            final String[] excludedApps = Attribute.stringToList(binding.getConfig().getInterfaceSection().getExcludedApplications());
 | 
				
			||||||
 | 
					            final AppListDialogFragment fragment = AppListDialogFragment.newInstance(excludedApps, this);
 | 
				
			||||||
 | 
					            fragment.show(fragmentManager, null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onSaveInstanceState(final Bundle outState) {
 | 
					    public void onSaveInstanceState(final Bundle outState) {
 | 
				
			||||||
        outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig());
 | 
					        outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig());
 | 
				
			||||||
@ -294,19 +312,4 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
 | 
				
			|||||||
        super.onViewStateRestored(savedInstanceState);
 | 
					        super.onViewStateRestored(savedInstanceState);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void onRequestSetExcludedApplications(@SuppressWarnings("unused") final View view) {
 | 
					 | 
				
			||||||
        final FragmentManager fragmentManager = getFragmentManager();
 | 
					 | 
				
			||||||
        if (fragmentManager != null && binding != null) {
 | 
					 | 
				
			||||||
            final String[] excludedApps = Attribute.stringToList(binding.getConfig().getInterfaceSection().getExcludedApplications());
 | 
					 | 
				
			||||||
            final AppListDialogFragment fragment = AppListDialogFragment.newInstance(excludedApps, this);
 | 
					 | 
				
			||||||
            fragment.show(fragmentManager, null);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    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));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,13 +38,11 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding;
 | 
				
			|||||||
import com.wireguard.android.databinding.TunnelListItemBinding;
 | 
					import com.wireguard.android.databinding.TunnelListItemBinding;
 | 
				
			||||||
import com.wireguard.android.model.Tunnel;
 | 
					import com.wireguard.android.model.Tunnel;
 | 
				
			||||||
import com.wireguard.android.util.ExceptionLoggers;
 | 
					import com.wireguard.android.util.ExceptionLoggers;
 | 
				
			||||||
import com.wireguard.android.util.ObservableSortedKeyedList;
 | 
					 | 
				
			||||||
import com.wireguard.android.widget.MultiselectableRelativeLayout;
 | 
					import com.wireguard.android.widget.MultiselectableRelativeLayout;
 | 
				
			||||||
import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollListener;
 | 
					import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollListener;
 | 
				
			||||||
import com.wireguard.config.Config;
 | 
					import com.wireguard.config.Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.BufferedReader;
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.io.InputStreamReader;
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
import java.nio.charset.StandardCharsets;
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
@ -185,6 +183,19 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onActivityCreated(savedInstanceState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (savedInstanceState != null) {
 | 
				
			||||||
 | 
					            final Collection<Integer> checkedItems = savedInstanceState.getIntegerArrayList("CHECKED_ITEMS");
 | 
				
			||||||
 | 
					            if (checkedItems != null) {
 | 
				
			||||||
 | 
					                for (final Integer i : checkedItems)
 | 
				
			||||||
 | 
					                    actionModeListener.setItemChecked(i, true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
 | 
					    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
 | 
				
			||||||
        switch (requestCode) {
 | 
					        switch (requestCode) {
 | 
				
			||||||
@ -228,6 +239,14 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
        super.onDestroyView();
 | 
					        super.onDestroyView();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onPause() {
 | 
				
			||||||
 | 
					        if (binding != null) {
 | 
				
			||||||
 | 
					            binding.createMenu.collapse();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        super.onPause();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void onRequestCreateConfig(@SuppressWarnings("unused") final View view) {
 | 
					    public void onRequestCreateConfig(@SuppressWarnings("unused") final View view) {
 | 
				
			||||||
        startActivity(new Intent(getActivity(), TunnelCreatorActivity.class));
 | 
					        startActivity(new Intent(getActivity(), TunnelCreatorActivity.class));
 | 
				
			||||||
        if (binding != null)
 | 
					        if (binding != null)
 | 
				
			||||||
@ -255,15 +274,10 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onPause() {
 | 
					    public void onSaveInstanceState(final Bundle outState) {
 | 
				
			||||||
        if (binding != null) {
 | 
					        super.onSaveInstanceState(outState);
 | 
				
			||||||
            binding.createMenu.collapse();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        super.onPause();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private MultiselectableRelativeLayout viewForTunnel(final Tunnel tunnel, final List tunnels) {
 | 
					        outState.putIntegerArrayList("CHECKED_ITEMS", actionModeListener.getCheckedItems());
 | 
				
			||||||
        return (MultiselectableRelativeLayout)binding.tunnelList.findViewHolderForAdapterPosition(tunnels.indexOf(tunnel)).itemView;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -317,26 +331,6 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
            Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG).show();
 | 
					            Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG).show();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onSaveInstanceState(final Bundle outState) {
 | 
					 | 
				
			||||||
        super.onSaveInstanceState(outState);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        outState.putIntegerArrayList("CHECKED_ITEMS", actionModeListener.getCheckedItems());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        super.onActivityCreated(savedInstanceState);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (savedInstanceState != null) {
 | 
					 | 
				
			||||||
            final Collection<Integer> checkedItems = savedInstanceState.getIntegerArrayList("CHECKED_ITEMS");
 | 
					 | 
				
			||||||
            if (checkedItems != null) {
 | 
					 | 
				
			||||||
                for (final Integer i : checkedItems)
 | 
					 | 
				
			||||||
                    actionModeListener.setItemChecked(i, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
 | 
					    public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
        super.onViewStateRestored(savedInstanceState);
 | 
					        super.onViewStateRestored(savedInstanceState);
 | 
				
			||||||
@ -362,17 +356,25 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (actionMode != null)
 | 
					            if (actionMode != null)
 | 
				
			||||||
                ((MultiselectableRelativeLayout)binding.getRoot()).setMultiSelected(actionModeListener.checkedItems.contains(position));
 | 
					                ((MultiselectableRelativeLayout) binding.getRoot()).setMultiSelected(actionModeListener.checkedItems.contains(position));
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                ((MultiselectableRelativeLayout)binding.getRoot()).setSingleSelected(getSelectedTunnel() == tunnel);
 | 
					                ((MultiselectableRelativeLayout) binding.getRoot()).setSingleSelected(getSelectedTunnel() == tunnel);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private MultiselectableRelativeLayout viewForTunnel(final Tunnel tunnel, final List tunnels) {
 | 
				
			||||||
 | 
					        return (MultiselectableRelativeLayout) binding.tunnelList.findViewHolderForAdapterPosition(tunnels.indexOf(tunnel)).itemView;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Nullable private Resources resources;
 | 
					        @Nullable private Resources resources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ArrayList<Integer> getCheckedItems() {
 | 
				
			||||||
 | 
					            return new ArrayList<>(checkedItems);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
 | 
					        public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
 | 
				
			||||||
            switch (item.getItemId()) {
 | 
					            switch (item.getItemId()) {
 | 
				
			||||||
@ -425,12 +427,10 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
            binding.tunnelList.getAdapter().notifyDataSetChanged();
 | 
					            binding.tunnelList.getAdapter().notifyDataSetChanged();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void toggleItemChecked(final int position) {
 | 
					        @Override
 | 
				
			||||||
            setItemChecked(position, !checkedItems.contains(position));
 | 
					        public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {
 | 
				
			||||||
        }
 | 
					            updateTitle(mode);
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
        public ArrayList<Integer> getCheckedItems() {
 | 
					 | 
				
			||||||
            return new ArrayList<>(checkedItems);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void setItemChecked(final int position, final boolean checked) {
 | 
					        void setItemChecked(final int position, final boolean checked) {
 | 
				
			||||||
@ -454,10 +454,8 @@ public class TunnelListFragment extends BaseFragment {
 | 
				
			|||||||
            updateTitle(actionMode);
 | 
					            updateTitle(actionMode);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        void toggleItemChecked(final int position) {
 | 
				
			||||||
        public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {
 | 
					            setItemChecked(position, !checkedItems.contains(position));
 | 
				
			||||||
            updateTitle(mode);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void updateTitle(@Nullable final ActionMode mode) {
 | 
					        private void updateTitle(@Nullable final ActionMode mode) {
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,11 @@ public class ApplicationData extends BaseObservable implements Keyed<String> {
 | 
				
			|||||||
        return icon;
 | 
					        return icon;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getKey() {
 | 
				
			||||||
 | 
					        return name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getName() {
 | 
					    public String getName() {
 | 
				
			||||||
        return name;
 | 
					        return name;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -47,9 +52,4 @@ public class ApplicationData extends BaseObservable implements Keyed<String> {
 | 
				
			|||||||
        this.excludedFromTunnel = excludedFromTunnel;
 | 
					        this.excludedFromTunnel = excludedFromTunnel;
 | 
				
			||||||
        notifyPropertyChanged(BR.excludedFromTunnel);
 | 
					        notifyPropertyChanged(BR.excludedFromTunnel);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getKey() {
 | 
					 | 
				
			||||||
        return name;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -151,5 +151,6 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static class Statistics extends BaseObservable { }
 | 
					    public static class Statistics extends BaseObservable {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -44,19 +44,28 @@ public final class TunnelManager extends BaseObservable {
 | 
				
			|||||||
    private static final String KEY_LAST_USED_TUNNEL = "last_used_tunnel";
 | 
					    private static final String KEY_LAST_USED_TUNNEL = "last_used_tunnel";
 | 
				
			||||||
    private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot";
 | 
					    private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot";
 | 
				
			||||||
    private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
 | 
					    private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
 | 
				
			||||||
 | 
					    private final CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> completableTunnels = new CompletableFuture<>();
 | 
				
			||||||
    private final ConfigStore configStore;
 | 
					    private final ConfigStore configStore;
 | 
				
			||||||
    private final Context context = Application.get();
 | 
					    private final Context context = Application.get();
 | 
				
			||||||
    private final CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> completableTunnels = new CompletableFuture<>();
 | 
					 | 
				
			||||||
    private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR);
 | 
					 | 
				
			||||||
    @Nullable private Tunnel lastUsedTunnel;
 | 
					 | 
				
			||||||
    private boolean haveLoaded;
 | 
					 | 
				
			||||||
    private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
 | 
					    private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
 | 
				
			||||||
 | 
					    private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR);
 | 
				
			||||||
 | 
					    private boolean haveLoaded;
 | 
				
			||||||
 | 
					    @Nullable private Tunnel lastUsedTunnel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public TunnelManager(final ConfigStore configStore) {
 | 
					    public TunnelManager(final ConfigStore configStore) {
 | 
				
			||||||
        this.configStore = configStore;
 | 
					        this.configStore = configStore;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static CompletionStage<State> getTunnelState(final Tunnel tunnel) {
 | 
				
			||||||
 | 
					        return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getState(tunnel))
 | 
				
			||||||
 | 
					                .thenApply(tunnel::onStateChanged);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static CompletionStage<Statistics> getTunnelStatistics(final Tunnel tunnel) {
 | 
				
			||||||
 | 
					        return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getStatistics(tunnel))
 | 
				
			||||||
 | 
					                .thenApply(tunnel::onStatisticsChanged);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Tunnel addToList(final String name, @Nullable 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);
 | 
				
			||||||
@ -112,16 +121,6 @@ public final class TunnelManager extends BaseObservable {
 | 
				
			|||||||
                .thenApply(tunnel::onConfigChanged);
 | 
					                .thenApply(tunnel::onConfigChanged);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static CompletionStage<State> getTunnelState(final Tunnel tunnel) {
 | 
					 | 
				
			||||||
        return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getState(tunnel))
 | 
					 | 
				
			||||||
                .thenApply(tunnel::onStateChanged);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static CompletionStage<Statistics> getTunnelStatistics(final Tunnel tunnel) {
 | 
					 | 
				
			||||||
        return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getStatistics(tunnel))
 | 
					 | 
				
			||||||
                .thenApply(tunnel::onStatisticsChanged);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> getTunnels() {
 | 
					    public CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> getTunnels() {
 | 
				
			||||||
        return completableTunnels;
 | 
					        return completableTunnels;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -50,7 +50,8 @@ public class VersionPreference extends Preference {
 | 
				
			|||||||
        intent.setData(Uri.parse("https://www.wireguard.com/"));
 | 
					        intent.setData(Uri.parse("https://www.wireguard.com/"));
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            getContext().startActivity(intent);
 | 
					            getContext().startActivity(intent);
 | 
				
			||||||
        } catch (final ActivityNotFoundException ignored) { }
 | 
					        } catch (final ActivityNotFoundException ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,8 @@ 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;
 | 
				
			||||||
 | 
				
			|||||||
@ -26,11 +26,10 @@ import java.util.List;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public final class ToolsInstaller {
 | 
					public final class ToolsInstaller {
 | 
				
			||||||
    public static final int ERROR = 0x0;
 | 
					    public static final int ERROR = 0x0;
 | 
				
			||||||
    public static final int YES = 0x1;
 | 
					 | 
				
			||||||
    public static final int NO = 0x2;
 | 
					 | 
				
			||||||
    public static final int MAGISK = 0x4;
 | 
					    public static final int MAGISK = 0x4;
 | 
				
			||||||
 | 
					    public static final int NO = 0x2;
 | 
				
			||||||
    public static final int SYSTEM = 0x8;
 | 
					    public static final int SYSTEM = 0x8;
 | 
				
			||||||
 | 
					    public static final int YES = 0x1;
 | 
				
			||||||
    private static final String[][] EXECUTABLES = {
 | 
					    private static final String[][] EXECUTABLES = {
 | 
				
			||||||
            {"libwg.so", "wg"},
 | 
					            {"libwg.so", "wg"},
 | 
				
			||||||
            {"libwg-quick.so", "wg-quick"},
 | 
					            {"libwg-quick.so", "wg-quick"},
 | 
				
			||||||
@ -107,34 +106,8 @@ public final class ToolsInstaller {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean willInstallAsMagiskModule() {
 | 
					    public int install() throws NoRootException {
 | 
				
			||||||
        synchronized (lock) {
 | 
					        return willInstallAsMagiskModule() ? installMagisk() : installSystem();
 | 
				
			||||||
            if (installAsMagiskModule == null) {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.core/mirror -a -d /sbin/.core/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
 | 
					 | 
				
			||||||
                } catch (final Exception ignored) {
 | 
					 | 
				
			||||||
                    installAsMagiskModule = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return installAsMagiskModule;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private int installSystem() throws NoRootException {
 | 
					 | 
				
			||||||
        if (INSTALL_DIR == null)
 | 
					 | 
				
			||||||
            return OsConstants.ENOENT;
 | 
					 | 
				
			||||||
        final StringBuilder script = new StringBuilder("set -ex; ");
 | 
					 | 
				
			||||||
        script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; ");
 | 
					 | 
				
			||||||
        for (final String[] names : EXECUTABLES) {
 | 
					 | 
				
			||||||
            final File destination = new File(INSTALL_DIR, names[1]);
 | 
					 | 
				
			||||||
            script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
 | 
					 | 
				
			||||||
                    new File(nativeLibraryDir, names[0]), destination, destination, destination));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
 | 
					 | 
				
			||||||
        } catch (final IOException ignored) {
 | 
					 | 
				
			||||||
            return ERROR;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private int installMagisk() throws NoRootException {
 | 
					    private int installMagisk() throws NoRootException {
 | 
				
			||||||
@ -158,8 +131,21 @@ public final class ToolsInstaller {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int install() throws NoRootException {
 | 
					    private int installSystem() throws NoRootException {
 | 
				
			||||||
        return willInstallAsMagiskModule() ? installMagisk() : installSystem();
 | 
					        if (INSTALL_DIR == null)
 | 
				
			||||||
 | 
					            return OsConstants.ENOENT;
 | 
				
			||||||
 | 
					        final StringBuilder script = new StringBuilder("set -ex; ");
 | 
				
			||||||
 | 
					        script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; ");
 | 
				
			||||||
 | 
					        for (final String[] names : EXECUTABLES) {
 | 
				
			||||||
 | 
					            final File destination = new File(INSTALL_DIR, names[1]);
 | 
				
			||||||
 | 
					            script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
 | 
				
			||||||
 | 
					                    new File(nativeLibraryDir, names[0]), destination, destination, destination));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
 | 
				
			||||||
 | 
					        } catch (final IOException ignored) {
 | 
				
			||||||
 | 
					            return ERROR;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int symlink() throws NoRootException {
 | 
					    public int symlink() throws NoRootException {
 | 
				
			||||||
@ -184,4 +170,17 @@ public final class ToolsInstaller {
 | 
				
			|||||||
            return OsConstants.EXIT_FAILURE;
 | 
					            return OsConstants.EXIT_FAILURE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean willInstallAsMagiskModule() {
 | 
				
			||||||
 | 
					        synchronized (lock) {
 | 
				
			||||||
 | 
					            if (installAsMagiskModule == null) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.core/mirror -a -d /sbin/.core/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
 | 
				
			||||||
 | 
					                } catch (final Exception ignored) {
 | 
				
			||||||
 | 
					                    installAsMagiskModule = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return installAsMagiskModule;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,23 +12,25 @@ import android.widget.RelativeLayout;
 | 
				
			|||||||
import com.wireguard.android.R;
 | 
					import com.wireguard.android.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MultiselectableRelativeLayout extends RelativeLayout {
 | 
					public class MultiselectableRelativeLayout extends RelativeLayout {
 | 
				
			||||||
 | 
					    private static final int[] STATE_MULTISELECTED = {R.attr.state_multiselected};
 | 
				
			||||||
 | 
					    private boolean multiselected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MultiselectableRelativeLayout(final Context context) {
 | 
					    public MultiselectableRelativeLayout(final Context context) {
 | 
				
			||||||
        super(context);
 | 
					        super(context);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs) {
 | 
					    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs) {
 | 
				
			||||||
        super(context, attrs);
 | 
					        super(context, attrs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
 | 
					    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
 | 
				
			||||||
        super(context, attrs, defStyleAttr);
 | 
					        super(context, attrs, defStyleAttr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
 | 
					    public MultiselectableRelativeLayout(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
 | 
				
			||||||
        super(context, attrs, defStyleAttr, defStyleRes);
 | 
					        super(context, attrs, defStyleAttr, defStyleRes);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final int[] STATE_MULTISELECTED = { R.attr.state_multiselected };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private boolean multiselected;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected int[] onCreateDrawableState(final int extraSpace) {
 | 
					    protected int[] onCreateDrawableState(final int extraSpace) {
 | 
				
			||||||
        if (multiselected) {
 | 
					        if (multiselected) {
 | 
				
			||||||
 | 
				
			|||||||
@ -31,95 +31,43 @@ import android.util.FloatProperty;
 | 
				
			|||||||
@TargetApi(Build.VERSION_CODES.N)
 | 
					@TargetApi(Build.VERSION_CODES.N)
 | 
				
			||||||
public class SlashDrawable extends Drawable {
 | 
					public class SlashDrawable extends Drawable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final float CORNER_RADIUS = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? 0f : 1f;
 | 
					 | 
				
			||||||
    private static final long QS_ANIM_LENGTH = 350;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Path mPath = new Path();
 | 
					 | 
				
			||||||
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // These values are derived in un-rotated (vertical) orientation
 | 
					 | 
				
			||||||
    private static final float SLASH_WIDTH = 1.8384776f;
 | 
					 | 
				
			||||||
    private static final float SLASH_HEIGHT = 28f;
 | 
					 | 
				
			||||||
    private static final float CENTER_X = 10.65f;
 | 
					    private static final float CENTER_X = 10.65f;
 | 
				
			||||||
    private static final float CENTER_Y = 11.869239f;
 | 
					    private static final float CENTER_Y = 11.869239f;
 | 
				
			||||||
    private static final float SCALE = 24f;
 | 
					    private static final float CORNER_RADIUS = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? 0f : 1f;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Bottom is derived during animation
 | 
					 | 
				
			||||||
    private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
 | 
					 | 
				
			||||||
    private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
 | 
					 | 
				
			||||||
    private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
 | 
					 | 
				
			||||||
    // 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 static final long QS_ANIM_LENGTH = 350;
 | 
				
			||||||
    private final Drawable mDrawable;
 | 
					    private static final float SCALE = 24f;
 | 
				
			||||||
    private final RectF mSlashRect = new RectF(0, 0, 0, 0);
 | 
					    private static final float SLASH_HEIGHT = 28f;
 | 
				
			||||||
    private float mRotation;
 | 
					    // These values are derived in un-rotated (vertical) orientation
 | 
				
			||||||
    private boolean mSlashed;
 | 
					    private static final float SLASH_WIDTH = 1.8384776f;
 | 
				
			||||||
    private boolean mAnimationEnabled = true;
 | 
					    // Bottom is derived during animation
 | 
				
			||||||
 | 
					    private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
 | 
				
			||||||
    public SlashDrawable(final Drawable d) {
 | 
					    private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
 | 
				
			||||||
        mDrawable = d;
 | 
					    private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int getIntrinsicHeight() {
 | 
					 | 
				
			||||||
        return mDrawable.getIntrinsicHeight();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int getIntrinsicWidth() {
 | 
					 | 
				
			||||||
        return mDrawable.getIntrinsicWidth();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onBoundsChange(final Rect bounds) {
 | 
					 | 
				
			||||||
        super.onBoundsChange(bounds);
 | 
					 | 
				
			||||||
        mDrawable.setBounds(bounds);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void setRotation(final float rotation) {
 | 
					 | 
				
			||||||
        if (mRotation == rotation)
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        mRotation = rotation;
 | 
					 | 
				
			||||||
        invalidateSelf();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void setAnimationEnabled(final boolean enabled) {
 | 
					 | 
				
			||||||
        mAnimationEnabled = enabled;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Animate this value on change
 | 
					 | 
				
			||||||
    private float mCurrentSlashLength;
 | 
					 | 
				
			||||||
    private static final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
 | 
					    private static final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void setValue(final SlashDrawable object, final float value) {
 | 
					 | 
				
			||||||
            object.mCurrentSlashLength = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public Float get(final SlashDrawable object) {
 | 
					        public Float get(final SlashDrawable object) {
 | 
				
			||||||
            return object.mCurrentSlashLength;
 | 
					            return object.mCurrentSlashLength;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("unchecked")
 | 
					        @Override
 | 
				
			||||||
    public void setSlashed(final boolean slashed) {
 | 
					        public void setValue(final SlashDrawable object, final float value) {
 | 
				
			||||||
        if (mSlashed == slashed) return;
 | 
					            object.mCurrentSlashLength = value;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        mSlashed = slashed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
 | 
					 | 
				
			||||||
        final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (mAnimationEnabled) {
 | 
					 | 
				
			||||||
            final ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
 | 
					 | 
				
			||||||
            anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
 | 
					 | 
				
			||||||
            anim.setDuration(QS_ANIM_LENGTH);
 | 
					 | 
				
			||||||
            anim.start();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            mCurrentSlashLength = end;
 | 
					 | 
				
			||||||
            invalidateSelf();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    private final Drawable mDrawable;
 | 
				
			||||||
 | 
					    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 | 
				
			||||||
 | 
					    private final Path mPath = new Path();
 | 
				
			||||||
 | 
					    private final RectF mSlashRect = new RectF(0, 0, 0, 0);
 | 
				
			||||||
 | 
					    private boolean mAnimationEnabled = true;
 | 
				
			||||||
 | 
					    // Animate this value on change
 | 
				
			||||||
 | 
					    private float mCurrentSlashLength;
 | 
				
			||||||
 | 
					    private float mRotation;
 | 
				
			||||||
 | 
					    private boolean mSlashed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SlashDrawable(final Drawable d) {
 | 
				
			||||||
 | 
					        mDrawable = d;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressWarnings("deprecation")
 | 
					    @SuppressWarnings("deprecation")
 | 
				
			||||||
@ -166,15 +114,76 @@ public class SlashDrawable extends Drawable {
 | 
				
			|||||||
        canvas.restore();
 | 
					        canvas.restore();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getIntrinsicHeight() {
 | 
				
			||||||
 | 
					        return mDrawable.getIntrinsicHeight();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getIntrinsicWidth() {
 | 
				
			||||||
 | 
					        return mDrawable.getIntrinsicWidth();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getOpacity() {
 | 
				
			||||||
 | 
					        return PixelFormat.OPAQUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onBoundsChange(final Rect bounds) {
 | 
				
			||||||
 | 
					        super.onBoundsChange(bounds);
 | 
				
			||||||
 | 
					        mDrawable.setBounds(bounds);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private float scale(final float frac, final int width) {
 | 
					    private float scale(final float frac, final int width) {
 | 
				
			||||||
        return frac * width;
 | 
					        return frac * width;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void updateRect(final float left, final float top, final float right, final float bottom) {
 | 
					    @Override
 | 
				
			||||||
        mSlashRect.left = left;
 | 
					    public void setAlpha(@IntRange(from = 0, to = 255) final int alpha) {
 | 
				
			||||||
        mSlashRect.top = top;
 | 
					        mDrawable.setAlpha(alpha);
 | 
				
			||||||
        mSlashRect.right = right;
 | 
					        mPaint.setAlpha(alpha);
 | 
				
			||||||
        mSlashRect.bottom = bottom;
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setAnimationEnabled(final boolean enabled) {
 | 
				
			||||||
 | 
					        mAnimationEnabled = enabled;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setColorFilter(@Nullable final ColorFilter colorFilter) {
 | 
				
			||||||
 | 
					        mDrawable.setColorFilter(colorFilter);
 | 
				
			||||||
 | 
					        mPaint.setColorFilter(colorFilter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setDrawableTintList(@Nullable final ColorStateList tint) {
 | 
				
			||||||
 | 
					        mDrawable.setTintList(tint);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRotation(final float rotation) {
 | 
				
			||||||
 | 
					        if (mRotation == rotation)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        mRotation = rotation;
 | 
				
			||||||
 | 
					        invalidateSelf();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    public void setSlashed(final boolean slashed) {
 | 
				
			||||||
 | 
					        if (mSlashed == slashed) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mSlashed = slashed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
 | 
				
			||||||
 | 
					        final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mAnimationEnabled) {
 | 
				
			||||||
 | 
					            final ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
 | 
				
			||||||
 | 
					            anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
 | 
				
			||||||
 | 
					            anim.setDuration(QS_ANIM_LENGTH);
 | 
				
			||||||
 | 
					            anim.start();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            mCurrentSlashLength = end;
 | 
				
			||||||
 | 
					            invalidateSelf();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -192,30 +201,16 @@ public class SlashDrawable extends Drawable {
 | 
				
			|||||||
        invalidateSelf();
 | 
					        invalidateSelf();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setDrawableTintList(@Nullable final ColorStateList tint) {
 | 
					 | 
				
			||||||
        mDrawable.setTintList(tint);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void setTintMode(final Mode tintMode) {
 | 
					    public void setTintMode(final Mode tintMode) {
 | 
				
			||||||
        super.setTintMode(tintMode);
 | 
					        super.setTintMode(tintMode);
 | 
				
			||||||
        mDrawable.setTintMode(tintMode);
 | 
					        mDrawable.setTintMode(tintMode);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    private void updateRect(final float left, final float top, final float right, final float bottom) {
 | 
				
			||||||
    public void setAlpha(@IntRange(from = 0, to = 255) final int alpha) {
 | 
					        mSlashRect.left = left;
 | 
				
			||||||
        mDrawable.setAlpha(alpha);
 | 
					        mSlashRect.top = top;
 | 
				
			||||||
        mPaint.setAlpha(alpha);
 | 
					        mSlashRect.right = right;
 | 
				
			||||||
    }
 | 
					        mSlashRect.bottom = bottom;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void setColorFilter(@Nullable final ColorFilter colorFilter) {
 | 
					 | 
				
			||||||
        mDrawable.setColorFilter(colorFilter);
 | 
					 | 
				
			||||||
        mPaint.setColorFilter(colorFilter);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int getOpacity() {
 | 
					 | 
				
			||||||
        return PixelFormat.OPAQUE;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,19 +18,13 @@ import android.view.View;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionsMenu> {
 | 
					public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionsMenu> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final long ANIMATION_DURATION = 250;
 | 
				
			||||||
 | 
					    private static final TimeInterpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public FloatingActionButtonBehavior(final Context context, final AttributeSet attrs) {
 | 
					    public FloatingActionButtonBehavior(final Context context, final AttributeSet attrs) {
 | 
				
			||||||
        super(context, attrs);
 | 
					        super(context, attrs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean layoutDependsOn(final CoordinatorLayout parent, final FloatingActionsMenu child,
 | 
					 | 
				
			||||||
                                   final View dependency) {
 | 
					 | 
				
			||||||
        return dependency instanceof Snackbar.SnackbarLayout;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final long ANIMATION_DURATION = 250;
 | 
					 | 
				
			||||||
    private static final TimeInterpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static void animateChange(final FloatingActionsMenu child, final float destination, final float fullSpan) {
 | 
					    private static void animateChange(final FloatingActionsMenu child, final float destination, final float fullSpan) {
 | 
				
			||||||
        final float origin = child.getBehaviorYTranslation();
 | 
					        final float origin = child.getBehaviorYTranslation();
 | 
				
			||||||
        if (Math.abs(destination - origin) < fullSpan / 2) {
 | 
					        if (Math.abs(destination - origin) < fullSpan / 2) {
 | 
				
			||||||
@ -40,17 +34,23 @@ public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<Flo
 | 
				
			|||||||
        final ValueAnimator animator = new ValueAnimator();
 | 
					        final ValueAnimator animator = new ValueAnimator();
 | 
				
			||||||
        animator.setFloatValues(origin, destination);
 | 
					        animator.setFloatValues(origin, destination);
 | 
				
			||||||
        animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
 | 
					        animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
 | 
				
			||||||
        animator.setDuration((long)(ANIMATION_DURATION * (Math.abs(destination - origin) / fullSpan)));
 | 
					        animator.setDuration((long) (ANIMATION_DURATION * (Math.abs(destination - origin) / fullSpan)));
 | 
				
			||||||
        animator.addListener(new AnimatorListenerAdapter() {
 | 
					        animator.addListener(new AnimatorListenerAdapter() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onAnimationEnd(final Animator a) {
 | 
					            public void onAnimationEnd(final Animator a) {
 | 
				
			||||||
                child.setBehaviorYTranslation(destination);
 | 
					                child.setBehaviorYTranslation(destination);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        animator.addUpdateListener(a -> child.setBehaviorYTranslation((float)a.getAnimatedValue()));
 | 
					        animator.addUpdateListener(a -> child.setBehaviorYTranslation((float) a.getAnimatedValue()));
 | 
				
			||||||
        animator.start();
 | 
					        animator.start();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean layoutDependsOn(final CoordinatorLayout parent, final FloatingActionsMenu child,
 | 
				
			||||||
 | 
					                                   final View dependency) {
 | 
				
			||||||
 | 
					        return dependency instanceof Snackbar.SnackbarLayout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean onDependentViewChanged(final CoordinatorLayout parent, final FloatingActionsMenu child,
 | 
					    public boolean onDependentViewChanged(final CoordinatorLayout parent, final FloatingActionsMenu child,
 | 
				
			||||||
                                          final View dependency) {
 | 
					                                          final View dependency) {
 | 
				
			||||||
 | 
				
			|||||||
@ -37,39 +37,38 @@ import android.widget.TextView;
 | 
				
			|||||||
import com.wireguard.android.R;
 | 
					import com.wireguard.android.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class FloatingActionsMenu extends ViewGroup {
 | 
					public class FloatingActionsMenu extends ViewGroup {
 | 
				
			||||||
    public static final int EXPAND_UP = 0;
 | 
					 | 
				
			||||||
    public static final int EXPAND_DOWN = 1;
 | 
					    public static final int EXPAND_DOWN = 1;
 | 
				
			||||||
    public static final int EXPAND_LEFT = 2;
 | 
					    public static final int EXPAND_LEFT = 2;
 | 
				
			||||||
    public static final int EXPAND_RIGHT = 3;
 | 
					    public static final int EXPAND_RIGHT = 3;
 | 
				
			||||||
 | 
					    public static final int EXPAND_UP = 0;
 | 
				
			||||||
    public static final int LABELS_ON_LEFT_SIDE = 0;
 | 
					    public static final int LABELS_ON_LEFT_SIDE = 0;
 | 
				
			||||||
    public static final int LABELS_ON_RIGHT_SIDE = 1;
 | 
					    public static final int LABELS_ON_RIGHT_SIDE = 1;
 | 
				
			||||||
 | 
					    private static final TimeInterpolator ALPHA_EXPAND_INTERPOLATOR = new DecelerateInterpolator();
 | 
				
			||||||
    private static final int ANIMATION_DURATION = 300;
 | 
					    private static final int ANIMATION_DURATION = 300;
 | 
				
			||||||
 | 
					    private static final boolean BROKEN_LABEL_STYLE = Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1 && Build.BRAND.equals("ASUS");
 | 
				
			||||||
    private static final float COLLAPSED_PLUS_ROTATION = 0f;
 | 
					    private static final float COLLAPSED_PLUS_ROTATION = 0f;
 | 
				
			||||||
 | 
					    private static final TimeInterpolator COLLAPSE_INTERPOLATOR = new DecelerateInterpolator(3f);
 | 
				
			||||||
    private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
 | 
					    private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
 | 
				
			||||||
    private static final TimeInterpolator EXPAND_INTERPOLATOR = new OvershootInterpolator();
 | 
					    private static final TimeInterpolator EXPAND_INTERPOLATOR = new OvershootInterpolator();
 | 
				
			||||||
    private static final TimeInterpolator COLLAPSE_INTERPOLATOR = new DecelerateInterpolator(3f);
 | 
					 | 
				
			||||||
    private static final TimeInterpolator ALPHA_EXPAND_INTERPOLATOR = new DecelerateInterpolator();
 | 
					 | 
				
			||||||
    private int mExpandDirection;
 | 
					 | 
				
			||||||
    private int mButtonSpacing;
 | 
					 | 
				
			||||||
    private int mLabelsMargin;
 | 
					 | 
				
			||||||
    private int mLabelsVerticalOffset;
 | 
					 | 
				
			||||||
    private boolean mExpanded;
 | 
					 | 
				
			||||||
    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);
 | 
				
			||||||
    @Nullable private FloatingActionButton mAddButton;
 | 
					    private final AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
 | 
				
			||||||
    @Nullable private RotatingDrawable mRotatingDrawable;
 | 
					 | 
				
			||||||
    private int mMaxButtonWidth;
 | 
					 | 
				
			||||||
    private int mMaxButtonHeight;
 | 
					 | 
				
			||||||
    private int mLabelsStyle;
 | 
					 | 
				
			||||||
    private int mLabelsPosition;
 | 
					 | 
				
			||||||
    private int mButtonsCount;
 | 
					 | 
				
			||||||
    @Nullable private TouchDelegateGroup mTouchDelegateGroup;
 | 
					 | 
				
			||||||
    @Nullable private OnFloatingActionsMenuUpdateListener mListener;
 | 
					 | 
				
			||||||
    private final Rect touchArea = new Rect(0, 0, 0, 0);
 | 
					    private final Rect touchArea = new Rect(0, 0, 0, 0);
 | 
				
			||||||
    private float scrollYTranslation;
 | 
					 | 
				
			||||||
    private float behaviorYTranslation;
 | 
					    private float behaviorYTranslation;
 | 
				
			||||||
 | 
					    @Nullable private FloatingActionButton mAddButton;
 | 
				
			||||||
 | 
					    private int mButtonSpacing;
 | 
				
			||||||
 | 
					    private int mButtonsCount;
 | 
				
			||||||
 | 
					    private int mExpandDirection;
 | 
				
			||||||
 | 
					    private boolean mExpanded;
 | 
				
			||||||
 | 
					    private int mLabelsMargin;
 | 
				
			||||||
 | 
					    private int mLabelsPosition;
 | 
				
			||||||
 | 
					    private int mLabelsStyle;
 | 
				
			||||||
 | 
					    private int mLabelsVerticalOffset;
 | 
				
			||||||
 | 
					    @Nullable private OnFloatingActionsMenuUpdateListener mListener;
 | 
				
			||||||
 | 
					    private int mMaxButtonHeight;
 | 
				
			||||||
 | 
					    private int mMaxButtonWidth;
 | 
				
			||||||
 | 
					    @Nullable private RotatingDrawable mRotatingDrawable;
 | 
				
			||||||
 | 
					    @Nullable private TouchDelegateGroup mTouchDelegateGroup;
 | 
				
			||||||
 | 
					    private float scrollYTranslation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public FloatingActionsMenu(final Context context) {
 | 
					    public FloatingActionsMenu(final Context context) {
 | 
				
			||||||
        this(context, null);
 | 
					        this(context, null);
 | 
				
			||||||
@ -85,51 +84,39 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
        init(context, attrs);
 | 
					        init(context, attrs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void init(final Context context, @Nullable final AttributeSet attributeSet) {
 | 
					    private static int adjustForOvershoot(final int dimension) {
 | 
				
			||||||
        mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing));
 | 
					        return dimension * 12 / 10;
 | 
				
			||||||
        mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
 | 
					    }
 | 
				
			||||||
        mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mTouchDelegateGroup = new TouchDelegateGroup(this);
 | 
					    public void addButton(final LabeledFloatingActionButton button) {
 | 
				
			||||||
        setTouchDelegate(mTouchDelegateGroup);
 | 
					        addView(button, mButtonsCount - 1);
 | 
				
			||||||
 | 
					        mButtonsCount++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
 | 
					        if (mLabelsStyle != 0) {
 | 
				
			||||||
        mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
 | 
					            createLabels();
 | 
				
			||||||
        mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
 | 
					 | 
				
			||||||
        mLabelsPosition = attr.getInt(R.styleable.FloatingActionsMenu_fab_labelsPosition, LABELS_ON_LEFT_SIDE);
 | 
					 | 
				
			||||||
        attr.recycle();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (mLabelsStyle != 0 && expandsHorizontally()) {
 | 
					 | 
				
			||||||
            throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        createAddButton(context);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public float getScrollYTranslation() {
 | 
					    public void collapse() {
 | 
				
			||||||
        return scrollYTranslation;
 | 
					        collapse(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void setScrollYTranslation(final float scrollYTranslation) {
 | 
					    private void collapse(final boolean immediately) {
 | 
				
			||||||
        this.scrollYTranslation = scrollYTranslation;
 | 
					        if (mExpanded) {
 | 
				
			||||||
        setTranslationY(behaviorYTranslation + scrollYTranslation);
 | 
					            mExpanded = false;
 | 
				
			||||||
 | 
					            mTouchDelegateGroup.setEnabled(false);
 | 
				
			||||||
 | 
					            mCollapseAnimation.setDuration(immediately ? 0 : ANIMATION_DURATION);
 | 
				
			||||||
 | 
					            mCollapseAnimation.start();
 | 
				
			||||||
 | 
					            mExpandAnimation.cancel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mListener != null) {
 | 
				
			||||||
 | 
					                mListener.onMenuCollapsed();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public float getBehaviorYTranslation() {
 | 
					    public void collapseImmediately() {
 | 
				
			||||||
        return behaviorYTranslation;
 | 
					        collapse(true);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void setBehaviorYTranslation(final float behaviorYTranslation) {
 | 
					 | 
				
			||||||
        this.behaviorYTranslation = behaviorYTranslation;
 | 
					 | 
				
			||||||
        setTranslationY(behaviorYTranslation + scrollYTranslation);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void setOnFloatingActionsMenuUpdateListener(final OnFloatingActionsMenuUpdateListener listener) {
 | 
					 | 
				
			||||||
        mListener = listener;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private boolean expandsHorizontally() {
 | 
					 | 
				
			||||||
        return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void createAddButton(final Context context) {
 | 
					    private void createAddButton(final Context context) {
 | 
				
			||||||
@ -156,87 +143,103 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
        mButtonsCount++;
 | 
					        mButtonsCount++;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void addButton(final LabeledFloatingActionButton button) {
 | 
					    private void createLabels() {
 | 
				
			||||||
        addView(button, mButtonsCount - 1);
 | 
					        final Context context = BROKEN_LABEL_STYLE ? getContext() : new ContextThemeWrapper(getContext(), mLabelsStyle);
 | 
				
			||||||
        mButtonsCount++;
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < mButtonsCount; i++) {
 | 
				
			||||||
 | 
					            final FloatingActionButton button = (FloatingActionButton) getChildAt(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (button instanceof LabeledFloatingActionButton) {
 | 
				
			||||||
 | 
					                final String title = ((LabeledFloatingActionButton) button).getTitle();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                final AppCompatTextView label = new AppCompatTextView(context);
 | 
				
			||||||
 | 
					                if (!BROKEN_LABEL_STYLE)
 | 
				
			||||||
 | 
					                    label.setTextAppearance(context, mLabelsStyle);
 | 
				
			||||||
 | 
					                label.setText(title);
 | 
				
			||||||
 | 
					                addView(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                button.setTag(R.id.fab_label, label);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void expand() {
 | 
				
			||||||
 | 
					        if (!mExpanded) {
 | 
				
			||||||
 | 
					            mExpanded = true;
 | 
				
			||||||
 | 
					            mTouchDelegateGroup.setEnabled(true);
 | 
				
			||||||
 | 
					            mCollapseAnimation.cancel();
 | 
				
			||||||
 | 
					            mExpandAnimation.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mListener != null) {
 | 
				
			||||||
 | 
					                mListener.onMenuExpanded();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean expandsHorizontally() {
 | 
				
			||||||
 | 
					        return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
 | 
				
			||||||
 | 
					        return new LayoutParams(super.generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {
 | 
				
			||||||
 | 
					        return new LayoutParams(super.generateLayoutParams(attrs));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams p) {
 | 
				
			||||||
 | 
					        return new LayoutParams(super.generateLayoutParams(p));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float getBehaviorYTranslation() {
 | 
				
			||||||
 | 
					        return behaviorYTranslation;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float getScrollYTranslation() {
 | 
				
			||||||
 | 
					        return scrollYTranslation;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void init(final Context context, @Nullable final AttributeSet attributeSet) {
 | 
				
			||||||
 | 
					        mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing));
 | 
				
			||||||
 | 
					        mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
 | 
				
			||||||
 | 
					        mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mTouchDelegateGroup = new TouchDelegateGroup(this);
 | 
				
			||||||
 | 
					        setTouchDelegate(mTouchDelegateGroup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
 | 
				
			||||||
 | 
					        mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
 | 
				
			||||||
 | 
					        mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
 | 
				
			||||||
 | 
					        mLabelsPosition = attr.getInt(R.styleable.FloatingActionsMenu_fab_labelsPosition, LABELS_ON_LEFT_SIDE);
 | 
				
			||||||
 | 
					        attr.recycle();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mLabelsStyle != 0 && expandsHorizontally()) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        createAddButton(context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isExpanded() {
 | 
				
			||||||
 | 
					        return mExpanded;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onFinishInflate() {
 | 
				
			||||||
 | 
					        super.onFinishInflate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bringChildToFront(mAddButton);
 | 
				
			||||||
 | 
					        mButtonsCount = getChildCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (mLabelsStyle != 0) {
 | 
					        if (mLabelsStyle != 0) {
 | 
				
			||||||
            createLabels();
 | 
					            createLabels();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void removeButton(final LabeledFloatingActionButton button) {
 | 
					 | 
				
			||||||
        removeView(button.getLabelView());
 | 
					 | 
				
			||||||
        removeView(button);
 | 
					 | 
				
			||||||
        button.setTag(R.id.fab_label, null);
 | 
					 | 
				
			||||||
        mButtonsCount--;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
 | 
					 | 
				
			||||||
        measureChildren(widthMeasureSpec, heightMeasureSpec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        int width = 0;
 | 
					 | 
				
			||||||
        int height = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mMaxButtonWidth = 0;
 | 
					 | 
				
			||||||
        mMaxButtonHeight = 0;
 | 
					 | 
				
			||||||
        int maxLabelWidth = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (int i = 0; i < mButtonsCount; i++) {
 | 
					 | 
				
			||||||
            final View child = getChildAt(i);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (child.getVisibility() == GONE) {
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch (mExpandDirection) {
 | 
					 | 
				
			||||||
                case EXPAND_UP:
 | 
					 | 
				
			||||||
                case EXPAND_DOWN:
 | 
					 | 
				
			||||||
                    mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
 | 
					 | 
				
			||||||
                    height += child.getMeasuredHeight();
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case EXPAND_LEFT:
 | 
					 | 
				
			||||||
                case EXPAND_RIGHT:
 | 
					 | 
				
			||||||
                    width += child.getMeasuredWidth();
 | 
					 | 
				
			||||||
                    mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!expandsHorizontally()) {
 | 
					 | 
				
			||||||
                final TextView label = (TextView) child.getTag(R.id.fab_label);
 | 
					 | 
				
			||||||
                if (label != null) {
 | 
					 | 
				
			||||||
                    maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (expandsHorizontally()) {
 | 
					 | 
				
			||||||
            height = mMaxButtonHeight;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        switch (mExpandDirection) {
 | 
					 | 
				
			||||||
            case EXPAND_UP:
 | 
					 | 
				
			||||||
            case EXPAND_DOWN:
 | 
					 | 
				
			||||||
                height += mButtonSpacing * (mButtonsCount - 1);
 | 
					 | 
				
			||||||
                height = adjustForOvershoot(height);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case EXPAND_LEFT:
 | 
					 | 
				
			||||||
            case EXPAND_RIGHT:
 | 
					 | 
				
			||||||
                width += mButtonSpacing * (mButtonsCount - 1);
 | 
					 | 
				
			||||||
                width = adjustForOvershoot(width);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        setMeasuredDimension(width, height);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static int adjustForOvershoot(final int dimension) {
 | 
					 | 
				
			||||||
        return dimension * 12 / 10;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
 | 
					    protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
 | 
				
			||||||
        switch (mExpandDirection) {
 | 
					        switch (mExpandDirection) {
 | 
				
			||||||
@ -367,115 +370,64 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
 | 
					    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
 | 
				
			||||||
        return new LayoutParams(super.generateDefaultLayoutParams());
 | 
					        measureChildren(widthMeasureSpec, heightMeasureSpec);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					        int width = 0;
 | 
				
			||||||
    public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {
 | 
					        int height = 0;
 | 
				
			||||||
        return new LayoutParams(super.generateLayoutParams(attrs));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					        mMaxButtonWidth = 0;
 | 
				
			||||||
    protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams p) {
 | 
					        mMaxButtonHeight = 0;
 | 
				
			||||||
        return new LayoutParams(super.generateLayoutParams(p));
 | 
					        int maxLabelWidth = 0;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onFinishInflate() {
 | 
					 | 
				
			||||||
        super.onFinishInflate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        bringChildToFront(mAddButton);
 | 
					 | 
				
			||||||
        mButtonsCount = getChildCount();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (mLabelsStyle != 0) {
 | 
					 | 
				
			||||||
            createLabels();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final boolean BROKEN_LABEL_STYLE = Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1 && Build.BRAND.equals("ASUS");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void createLabels() {
 | 
					 | 
				
			||||||
        final Context context = BROKEN_LABEL_STYLE ? getContext() : new ContextThemeWrapper(getContext(), mLabelsStyle);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (int i = 0; i < mButtonsCount; i++) {
 | 
					        for (int i = 0; i < mButtonsCount; i++) {
 | 
				
			||||||
            final FloatingActionButton button = (FloatingActionButton) getChildAt(i);
 | 
					            final View child = getChildAt(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (button instanceof LabeledFloatingActionButton) {
 | 
					            if (child.getVisibility() == GONE) {
 | 
				
			||||||
                final String title = ((LabeledFloatingActionButton) button).getTitle();
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                final AppCompatTextView label = new AppCompatTextView(context);
 | 
					            switch (mExpandDirection) {
 | 
				
			||||||
                if (!BROKEN_LABEL_STYLE)
 | 
					                case EXPAND_UP:
 | 
				
			||||||
                    label.setTextAppearance(context, mLabelsStyle);
 | 
					                case EXPAND_DOWN:
 | 
				
			||||||
                label.setText(title);
 | 
					                    mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
 | 
				
			||||||
                addView(label);
 | 
					                    height += child.getMeasuredHeight();
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case EXPAND_LEFT:
 | 
				
			||||||
 | 
					                case EXPAND_RIGHT:
 | 
				
			||||||
 | 
					                    width += child.getMeasuredWidth();
 | 
				
			||||||
 | 
					                    mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                button.setTag(R.id.fab_label, label);
 | 
					            if (!expandsHorizontally()) {
 | 
				
			||||||
 | 
					                final TextView label = (TextView) child.getTag(R.id.fab_label);
 | 
				
			||||||
 | 
					                if (label != null) {
 | 
				
			||||||
 | 
					                    maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void collapse() {
 | 
					        if (expandsHorizontally()) {
 | 
				
			||||||
        collapse(false);
 | 
					            height = mMaxButtonHeight;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void collapseImmediately() {
 | 
					 | 
				
			||||||
        collapse(true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void collapse(final boolean immediately) {
 | 
					 | 
				
			||||||
        if (mExpanded) {
 | 
					 | 
				
			||||||
            mExpanded = false;
 | 
					 | 
				
			||||||
            mTouchDelegateGroup.setEnabled(false);
 | 
					 | 
				
			||||||
            mCollapseAnimation.setDuration(immediately ? 0 : ANIMATION_DURATION);
 | 
					 | 
				
			||||||
            mCollapseAnimation.start();
 | 
					 | 
				
			||||||
            mExpandAnimation.cancel();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (mListener != null) {
 | 
					 | 
				
			||||||
                mListener.onMenuCollapsed();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void toggle() {
 | 
					 | 
				
			||||||
        if (mExpanded) {
 | 
					 | 
				
			||||||
            collapse();
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            expand();
 | 
					            width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void expand() {
 | 
					        switch (mExpandDirection) {
 | 
				
			||||||
        if (!mExpanded) {
 | 
					            case EXPAND_UP:
 | 
				
			||||||
            mExpanded = true;
 | 
					            case EXPAND_DOWN:
 | 
				
			||||||
            mTouchDelegateGroup.setEnabled(true);
 | 
					                height += mButtonSpacing * (mButtonsCount - 1);
 | 
				
			||||||
            mCollapseAnimation.cancel();
 | 
					                height = adjustForOvershoot(height);
 | 
				
			||||||
            mExpandAnimation.start();
 | 
					                break;
 | 
				
			||||||
 | 
					            case EXPAND_LEFT:
 | 
				
			||||||
            if (mListener != null) {
 | 
					            case EXPAND_RIGHT:
 | 
				
			||||||
                mListener.onMenuExpanded();
 | 
					                width += mButtonSpacing * (mButtonsCount - 1);
 | 
				
			||||||
            }
 | 
					                width = adjustForOvershoot(width);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean isExpanded() {
 | 
					        setMeasuredDimension(width, height);
 | 
				
			||||||
        return mExpanded;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void setEnabled(final boolean enabled) {
 | 
					 | 
				
			||||||
        super.setEnabled(enabled);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mAddButton.setEnabled(enabled);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Parcelable onSaveInstanceState() {
 | 
					 | 
				
			||||||
        final Parcelable superState = super.onSaveInstanceState();
 | 
					 | 
				
			||||||
        final SavedState savedState = new SavedState(superState);
 | 
					 | 
				
			||||||
        savedState.mExpanded = mExpanded;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return savedState;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -495,10 +447,55 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public interface OnFloatingActionsMenuUpdateListener {
 | 
					    @Override
 | 
				
			||||||
        void onMenuExpanded();
 | 
					    public Parcelable onSaveInstanceState() {
 | 
				
			||||||
 | 
					        final Parcelable superState = super.onSaveInstanceState();
 | 
				
			||||||
 | 
					        final SavedState savedState = new SavedState(superState);
 | 
				
			||||||
 | 
					        savedState.mExpanded = mExpanded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return savedState;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeButton(final LabeledFloatingActionButton button) {
 | 
				
			||||||
 | 
					        removeView(button.getLabelView());
 | 
				
			||||||
 | 
					        removeView(button);
 | 
				
			||||||
 | 
					        button.setTag(R.id.fab_label, null);
 | 
				
			||||||
 | 
					        mButtonsCount--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setBehaviorYTranslation(final float behaviorYTranslation) {
 | 
				
			||||||
 | 
					        this.behaviorYTranslation = behaviorYTranslation;
 | 
				
			||||||
 | 
					        setTranslationY(behaviorYTranslation + scrollYTranslation);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setEnabled(final boolean enabled) {
 | 
				
			||||||
 | 
					        super.setEnabled(enabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mAddButton.setEnabled(enabled);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setOnFloatingActionsMenuUpdateListener(final OnFloatingActionsMenuUpdateListener listener) {
 | 
				
			||||||
 | 
					        mListener = listener;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setScrollYTranslation(final float scrollYTranslation) {
 | 
				
			||||||
 | 
					        this.scrollYTranslation = scrollYTranslation;
 | 
				
			||||||
 | 
					        setTranslationY(behaviorYTranslation + scrollYTranslation);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void toggle() {
 | 
				
			||||||
 | 
					        if (mExpanded) {
 | 
				
			||||||
 | 
					            collapse();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            expand();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface OnFloatingActionsMenuUpdateListener {
 | 
				
			||||||
        void onMenuCollapsed();
 | 
					        void onMenuCollapsed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void onMenuExpanded();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static class RotatingDrawable extends LayerDrawable {
 | 
					    private static class RotatingDrawable extends LayerDrawable {
 | 
				
			||||||
@ -508,6 +505,14 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
            super(new Drawable[]{drawable});
 | 
					            super(new Drawable[]{drawable});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void draw(final Canvas canvas) {
 | 
				
			||||||
 | 
					            canvas.save();
 | 
				
			||||||
 | 
					            canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
 | 
				
			||||||
 | 
					            super.draw(canvas);
 | 
				
			||||||
 | 
					            canvas.restore();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @SuppressWarnings("UnusedDeclaration")
 | 
					        @SuppressWarnings("UnusedDeclaration")
 | 
				
			||||||
        public float getRotation() {
 | 
					        public float getRotation() {
 | 
				
			||||||
            return mRotation;
 | 
					            return mRotation;
 | 
				
			||||||
@ -519,14 +524,6 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
            mRotation = rotation;
 | 
					            mRotation = rotation;
 | 
				
			||||||
            invalidateSelf();
 | 
					            invalidateSelf();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void draw(final Canvas canvas) {
 | 
					 | 
				
			||||||
            canvas.save();
 | 
					 | 
				
			||||||
            canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
 | 
					 | 
				
			||||||
            super.draw(canvas);
 | 
					 | 
				
			||||||
            canvas.restore();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static class SavedState extends BaseSavedState {
 | 
					    public static class SavedState extends BaseSavedState {
 | 
				
			||||||
@ -562,10 +559,10 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private class LayoutParams extends ViewGroup.LayoutParams {
 | 
					    private class LayoutParams extends ViewGroup.LayoutParams {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private final ObjectAnimator mExpandDir = new ObjectAnimator();
 | 
					 | 
				
			||||||
        private final ObjectAnimator mExpandAlpha = new ObjectAnimator();
 | 
					 | 
				
			||||||
        private final ObjectAnimator mCollapseDir = new ObjectAnimator();
 | 
					 | 
				
			||||||
        private final ObjectAnimator mCollapseAlpha = new ObjectAnimator();
 | 
					        private final ObjectAnimator mCollapseAlpha = new ObjectAnimator();
 | 
				
			||||||
 | 
					        private final ObjectAnimator mCollapseDir = new ObjectAnimator();
 | 
				
			||||||
 | 
					        private final ObjectAnimator mExpandAlpha = new ObjectAnimator();
 | 
				
			||||||
 | 
					        private final ObjectAnimator mExpandDir = new ObjectAnimator();
 | 
				
			||||||
        private boolean animationsSetToPlay;
 | 
					        private boolean animationsSetToPlay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        LayoutParams(final ViewGroup.LayoutParams source) {
 | 
					        LayoutParams(final ViewGroup.LayoutParams source) {
 | 
				
			||||||
@ -596,6 +593,20 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void addLayerTypeListener(final Animator animator, final View view) {
 | 
				
			||||||
 | 
					            animator.addListener(new AnimatorListenerAdapter() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onAnimationEnd(final Animator animation) {
 | 
				
			||||||
 | 
					                    view.setLayerType(LAYER_TYPE_NONE, null);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onAnimationStart(final Animator animation) {
 | 
				
			||||||
 | 
					                    view.setLayerType(LAYER_TYPE_HARDWARE, null);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void setAnimationsTarget(final View view) {
 | 
					        public void setAnimationsTarget(final View view) {
 | 
				
			||||||
            mCollapseAlpha.setTarget(view);
 | 
					            mCollapseAlpha.setTarget(view);
 | 
				
			||||||
            mCollapseDir.setTarget(view);
 | 
					            mCollapseDir.setTarget(view);
 | 
				
			||||||
@ -614,19 +625,5 @@ public class FloatingActionsMenu extends ViewGroup {
 | 
				
			|||||||
                animationsSetToPlay = true;
 | 
					                animationsSetToPlay = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void addLayerTypeListener(final Animator animator, final View view) {
 | 
					 | 
				
			||||||
            animator.addListener(new AnimatorListenerAdapter() {
 | 
					 | 
				
			||||||
                @Override
 | 
					 | 
				
			||||||
                public void onAnimationEnd(final Animator animation) {
 | 
					 | 
				
			||||||
                    view.setLayerType(LAYER_TYPE_NONE, null);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                @Override
 | 
					 | 
				
			||||||
                public void onAnimationStart(final Animator animation) {
 | 
					 | 
				
			||||||
                    view.setLayerType(LAYER_TYPE_HARDWARE, null);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -29,13 +29,6 @@ public class TouchDelegateGroup extends TouchDelegate {
 | 
				
			|||||||
        mTouchDelegates.add(touchDelegate);
 | 
					        mTouchDelegates.add(touchDelegate);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void removeTouchDelegate(final TouchDelegate touchDelegate) {
 | 
					 | 
				
			||||||
        mTouchDelegates.remove(touchDelegate);
 | 
					 | 
				
			||||||
        if (mCurrentTouchDelegate == touchDelegate) {
 | 
					 | 
				
			||||||
            mCurrentTouchDelegate = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void clearTouchDelegates() {
 | 
					    public void clearTouchDelegates() {
 | 
				
			||||||
        mTouchDelegates.clear();
 | 
					        mTouchDelegates.clear();
 | 
				
			||||||
        mCurrentTouchDelegate = null;
 | 
					        mCurrentTouchDelegate = null;
 | 
				
			||||||
@ -72,6 +65,13 @@ public class TouchDelegateGroup extends TouchDelegate {
 | 
				
			|||||||
        return delegate != null && delegate.onTouchEvent(event);
 | 
					        return delegate != null && delegate.onTouchEvent(event);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeTouchDelegate(final TouchDelegate touchDelegate) {
 | 
				
			||||||
 | 
					        mTouchDelegates.remove(touchDelegate);
 | 
				
			||||||
 | 
					        if (mCurrentTouchDelegate == touchDelegate) {
 | 
				
			||||||
 | 
					            mCurrentTouchDelegate = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void setEnabled(final boolean enabled) {
 | 
					    public void setEnabled(final boolean enabled) {
 | 
				
			||||||
        mEnabled = enabled;
 | 
					        mEnabled = enabled;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -105,9 +105,9 @@ public class Config {
 | 
				
			|||||||
                return new Observable[size];
 | 
					                return new Observable[size];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        @Nullable private String name;
 | 
					 | 
				
			||||||
        private final Interface.Observable observableInterface;
 | 
					        private final Interface.Observable observableInterface;
 | 
				
			||||||
        private final ObservableList<Peer.Observable> observablePeers;
 | 
					        private final ObservableList<Peer.Observable> observablePeers;
 | 
				
			||||||
 | 
					        @Nullable private String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Observable(@Nullable final Config parent, @Nullable final String name) {
 | 
					        public Observable(@Nullable final Config parent, @Nullable final String name) {
 | 
				
			||||||
            this.name = name;
 | 
					            this.name = name;
 | 
				
			||||||
 | 
				
			|||||||
@ -28,12 +28,12 @@ import java.util.List;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class Interface {
 | 
					public class Interface {
 | 
				
			||||||
    private final List<InetNetwork> addressList;
 | 
					    private final List<InetNetwork> addressList;
 | 
				
			||||||
 | 
					    private final Context context = Application.get();
 | 
				
			||||||
    private final List<InetAddress> dnsList;
 | 
					    private final List<InetAddress> dnsList;
 | 
				
			||||||
    private final List<String> excludedApplications;
 | 
					    private final List<String> excludedApplications;
 | 
				
			||||||
    @Nullable private Keypair keypair;
 | 
					    @Nullable private Keypair keypair;
 | 
				
			||||||
    private int listenPort;
 | 
					    private int listenPort;
 | 
				
			||||||
    private int mtu;
 | 
					    private int mtu;
 | 
				
			||||||
    private final Context context = Application.get();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Interface() {
 | 
					    public Interface() {
 | 
				
			||||||
        addressList = new ArrayList<>();
 | 
					        addressList = new ArrayList<>();
 | 
				
			||||||
@ -94,6 +94,10 @@ public class Interface {
 | 
				
			|||||||
        return dnsList.toArray(new InetAddress[dnsList.size()]);
 | 
					        return dnsList.toArray(new InetAddress[dnsList.size()]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String[] getExcludedApplications() {
 | 
				
			||||||
 | 
					        return excludedApplications.toArray(new String[excludedApplications.size()]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private String getExcludedApplicationsString() {
 | 
					    private String getExcludedApplicationsString() {
 | 
				
			||||||
        if (excludedApplications.isEmpty())
 | 
					        if (excludedApplications.isEmpty())
 | 
				
			||||||
@ -101,10 +105,6 @@ public class Interface {
 | 
				
			|||||||
        return Attribute.iterableToString(excludedApplications);
 | 
					        return Attribute.iterableToString(excludedApplications);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String[] getExcludedApplications() {
 | 
					 | 
				
			||||||
        return excludedApplications.toArray(new String[excludedApplications.size()]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public int getListenPort() {
 | 
					    public int getListenPort() {
 | 
				
			||||||
        return listenPort;
 | 
					        return listenPort;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -37,11 +37,11 @@ import java9.lang.Iterables;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class Peer {
 | 
					public class Peer {
 | 
				
			||||||
    private final List<InetNetwork> allowedIPsList;
 | 
					    private final List<InetNetwork> allowedIPsList;
 | 
				
			||||||
 | 
					    private final Context context = Application.get();
 | 
				
			||||||
    @Nullable private InetEndpoint endpoint;
 | 
					    @Nullable private InetEndpoint endpoint;
 | 
				
			||||||
    private int persistentKeepalive;
 | 
					    private int persistentKeepalive;
 | 
				
			||||||
    @Nullable private String preSharedKey;
 | 
					    @Nullable private String preSharedKey;
 | 
				
			||||||
    @Nullable private String publicKey;
 | 
					    @Nullable private String publicKey;
 | 
				
			||||||
    private final Context context = Application.get();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Peer() {
 | 
					    public Peer() {
 | 
				
			||||||
        allowedIPsList = new ArrayList<>();
 | 
					        allowedIPsList = new ArrayList<>();
 | 
				
			||||||
@ -201,13 +201,15 @@ public class Peer {
 | 
				
			|||||||
                return new Observable[size];
 | 
					                return new Observable[size];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        private static final List<String> DEFAULT_ROUTE_MOD_RFC1918_V4 = Arrays.asList("0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14", "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7", "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4");
 | 
				
			||||||
 | 
					        private static final String DEFAULT_ROUTE_V4 = "0.0.0.0/0";
 | 
				
			||||||
 | 
					        private final List<String> interfaceDNSRoutes = new ArrayList<>();
 | 
				
			||||||
        @Nullable private String allowedIPs;
 | 
					        @Nullable private String allowedIPs;
 | 
				
			||||||
        @Nullable private String endpoint;
 | 
					        @Nullable private String endpoint;
 | 
				
			||||||
 | 
					        private int numSiblings;
 | 
				
			||||||
        @Nullable private String persistentKeepalive;
 | 
					        @Nullable private String persistentKeepalive;
 | 
				
			||||||
        @Nullable private String preSharedKey;
 | 
					        @Nullable private String preSharedKey;
 | 
				
			||||||
        @Nullable private String publicKey;
 | 
					        @Nullable private String publicKey;
 | 
				
			||||||
        private final List<String> interfaceDNSRoutes = new ArrayList<>();
 | 
					 | 
				
			||||||
        private int numSiblings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Observable(final Peer parent) {
 | 
					        public Observable(final Peer parent) {
 | 
				
			||||||
            loadData(parent);
 | 
					            loadData(parent);
 | 
				
			||||||
@ -244,22 +246,9 @@ public class Peer {
 | 
				
			|||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static final String DEFAULT_ROUTE_V4 = "0.0.0.0/0";
 | 
					        @Bindable @Nullable
 | 
				
			||||||
        private static final List<String> DEFAULT_ROUTE_MOD_RFC1918_V4 = Arrays.asList("0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14", "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7", "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4");
 | 
					        public String getAllowedIPs() {
 | 
				
			||||||
 | 
					            return allowedIPs;
 | 
				
			||||||
        public void toggleExcludePrivateIPs() {
 | 
					 | 
				
			||||||
            final Collection<String> ips = new HashSet<>(Arrays.asList(Attribute.stringToList(allowedIPs)));
 | 
					 | 
				
			||||||
            final boolean hasDefaultRoute = ips.contains(DEFAULT_ROUTE_V4);
 | 
					 | 
				
			||||||
            final boolean hasDefaultRouteModRFC1918 = ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
					 | 
				
			||||||
            if ((!hasDefaultRoute && !hasDefaultRouteModRFC1918) || numSiblings > 0)
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            Iterables.removeIf(ips, ip -> !ip.contains(":"));
 | 
					 | 
				
			||||||
            if (hasDefaultRoute) {
 | 
					 | 
				
			||||||
                ips.addAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
					 | 
				
			||||||
                ips.addAll(interfaceDNSRoutes);
 | 
					 | 
				
			||||||
            } else if (hasDefaultRouteModRFC1918)
 | 
					 | 
				
			||||||
                ips.add(DEFAULT_ROUTE_V4);
 | 
					 | 
				
			||||||
            setAllowedIPs(Attribute.iterableToString(ips));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Bindable
 | 
					        @Bindable
 | 
				
			||||||
@ -268,21 +257,16 @@ public class Peer {
 | 
				
			|||||||
            return numSiblings == 0 && (ips.contains(DEFAULT_ROUTE_V4) || ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4));
 | 
					            return numSiblings == 0 && (ips.contains(DEFAULT_ROUTE_V4) || ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Bindable
 | 
					 | 
				
			||||||
        public boolean getIsExcludePrivateIPsOn() {
 | 
					 | 
				
			||||||
            return numSiblings == 0 && Arrays.asList(Attribute.stringToList(allowedIPs)).containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Bindable @Nullable
 | 
					 | 
				
			||||||
        public String getAllowedIPs() {
 | 
					 | 
				
			||||||
            return allowedIPs;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Bindable @Nullable
 | 
					        @Bindable @Nullable
 | 
				
			||||||
        public String getEndpoint() {
 | 
					        public String getEndpoint() {
 | 
				
			||||||
            return endpoint;
 | 
					            return endpoint;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Bindable
 | 
				
			||||||
 | 
					        public boolean getIsExcludePrivateIPsOn() {
 | 
				
			||||||
 | 
					            return numSiblings == 0 && Arrays.asList(Attribute.stringToList(allowedIPs)).containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Bindable @Nullable
 | 
					        @Bindable @Nullable
 | 
				
			||||||
        public String getPersistentKeepalive() {
 | 
					        public String getPersistentKeepalive() {
 | 
				
			||||||
            return persistentKeepalive;
 | 
					            return persistentKeepalive;
 | 
				
			||||||
@ -318,21 +302,6 @@ public class Peer {
 | 
				
			|||||||
            notifyPropertyChanged(BR.endpoint);
 | 
					            notifyPropertyChanged(BR.endpoint);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void setPersistentKeepalive(final String persistentKeepalive) {
 | 
					 | 
				
			||||||
            this.persistentKeepalive = persistentKeepalive;
 | 
					 | 
				
			||||||
            notifyPropertyChanged(BR.persistentKeepalive);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setPreSharedKey(final String preSharedKey) {
 | 
					 | 
				
			||||||
            this.preSharedKey = preSharedKey;
 | 
					 | 
				
			||||||
            notifyPropertyChanged(BR.preSharedKey);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setPublicKey(final String publicKey) {
 | 
					 | 
				
			||||||
            this.publicKey = publicKey;
 | 
					 | 
				
			||||||
            notifyPropertyChanged(BR.publicKey);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setInterfaceDNSRoutes(@Nullable final String dnsServers) {
 | 
					        public void setInterfaceDNSRoutes(@Nullable final String dnsServers) {
 | 
				
			||||||
            final Collection<String> ips = new HashSet<>(Arrays.asList(Attribute.stringToList(allowedIPs)));
 | 
					            final Collection<String> ips = new HashSet<>(Arrays.asList(Attribute.stringToList(allowedIPs)));
 | 
				
			||||||
            final boolean modifyAllowedIPs = ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
					            final boolean modifyAllowedIPs = ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
				
			||||||
@ -354,6 +323,36 @@ public class Peer {
 | 
				
			|||||||
            notifyPropertyChanged(BR.isExcludePrivateIPsOn);
 | 
					            notifyPropertyChanged(BR.isExcludePrivateIPsOn);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void setPersistentKeepalive(final String persistentKeepalive) {
 | 
				
			||||||
 | 
					            this.persistentKeepalive = persistentKeepalive;
 | 
				
			||||||
 | 
					            notifyPropertyChanged(BR.persistentKeepalive);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void setPreSharedKey(final String preSharedKey) {
 | 
				
			||||||
 | 
					            this.preSharedKey = preSharedKey;
 | 
				
			||||||
 | 
					            notifyPropertyChanged(BR.preSharedKey);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void setPublicKey(final String publicKey) {
 | 
				
			||||||
 | 
					            this.publicKey = publicKey;
 | 
				
			||||||
 | 
					            notifyPropertyChanged(BR.publicKey);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void toggleExcludePrivateIPs() {
 | 
				
			||||||
 | 
					            final Collection<String> ips = new HashSet<>(Arrays.asList(Attribute.stringToList(allowedIPs)));
 | 
				
			||||||
 | 
					            final boolean hasDefaultRoute = ips.contains(DEFAULT_ROUTE_V4);
 | 
				
			||||||
 | 
					            final boolean hasDefaultRouteModRFC1918 = ips.containsAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
				
			||||||
 | 
					            if ((!hasDefaultRoute && !hasDefaultRouteModRFC1918) || numSiblings > 0)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            Iterables.removeIf(ips, ip -> !ip.contains(":"));
 | 
				
			||||||
 | 
					            if (hasDefaultRoute) {
 | 
				
			||||||
 | 
					                ips.addAll(DEFAULT_ROUTE_MOD_RFC1918_V4);
 | 
				
			||||||
 | 
					                ips.addAll(interfaceDNSRoutes);
 | 
				
			||||||
 | 
					            } else if (hasDefaultRouteModRFC1918)
 | 
				
			||||||
 | 
					                ips.add(DEFAULT_ROUTE_V4);
 | 
				
			||||||
 | 
					            setAllowedIPs(Attribute.iterableToString(ips));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void writeToParcel(final Parcel dest, final int flags) {
 | 
					        public void writeToParcel(final Parcel dest, final int flags) {
 | 
				
			||||||
            dest.writeString(allowedIPs);
 | 
					            dest.writeString(allowedIPs);
 | 
				
			||||||
 | 
				
			|||||||
@ -22,4 +22,5 @@ import javax.annotation.meta.TypeQualifierDefault;
 | 
				
			|||||||
@Nonnull
 | 
					@Nonnull
 | 
				
			||||||
@TypeQualifierDefault({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
 | 
					@TypeQualifierDefault({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
 | 
				
			||||||
@Retention(RetentionPolicy.RUNTIME)
 | 
					@Retention(RetentionPolicy.RUNTIME)
 | 
				
			||||||
public @interface NonNullForAll { }
 | 
					public @interface NonNullForAll {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    android:viewportWidth="24"
 | 
					 | 
				
			||||||
    android:viewportHeight="24"
 | 
					 | 
				
			||||||
    android:width="24dp"
 | 
					    android:width="24dp"
 | 
				
			||||||
    android:height="24dp">
 | 
					    android:height="24dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="24"
 | 
				
			||||||
 | 
					    android:viewportWidth="24">
 | 
				
			||||||
    <path
 | 
					    <path
 | 
				
			||||||
        android:fillColor="?android:attr/colorForeground"
 | 
					        android:fillColor="?android:attr/colorForeground"
 | 
				
			||||||
        android:pathData="M3 5L5 5 5 3C3.9 3 3 3.9 3 5Zm0 8l2 0 0 -2 -2 0 0 2zm4 8l2 0 0 -2 -2 0 0 2zM3 9L5 9 5 7 3 7 3 9Zm10 -6l-2 0 0 2 2 0 0 -2zm6 0l0 2 2 0C21 3.9 20.1 3 19 3ZM5 21L5 19 3 19c0 1.1 0.9 2 2 2zm-2 -4l2 0 0 -2 -2 0 0 2zM9 3L7 3 7 5 9 5 9 3Zm2 18l2 0 0 -2 -2 0 0 2zm8 -8l2 0 0 -2 -2 0 0 2zm0 8c1.1 0 2 -0.9 2 -2l-2 0 0 2zm0 -12l2 0 0 -2 -2 0 0 2zm0 8l2 0 0 -2 -2 0 0 2zm-4 4l2 0 0 -2 -2 0 0 2zm0 -16l2 0 0 -2 -2 0 0 2zM7 17L17 17 17 7 7 7 7 17Zm2 -8l6 0 0 6 -6 0 0 -6z" />
 | 
					        android:pathData="M3 5L5 5 5 3C3.9 3 3 3.9 3 5Zm0 8l2 0 0 -2 -2 0 0 2zm4 8l2 0 0 -2 -2 0 0 2zM3 9L5 9 5 7 3 7 3 9Zm10 -6l-2 0 0 2 2 0 0 -2zm6 0l0 2 2 0C21 3.9 20.1 3 19 3ZM5 21L5 19 3 19c0 1.1 0.9 2 2 2zm-2 -4l2 0 0 -2 -2 0 0 2zM9 3L7 3 7 5 9 5 9 3Zm2 18l2 0 0 -2 -2 0 0 2zm8 -8l2 0 0 -2 -2 0 0 2zm0 8c1.1 0 2 -0.9 2 -2l-2 0 0 2zm0 -12l2 0 0 -2 -2 0 0 2zm0 8l2 0 0 -2 -2 0 0 2zm-4 4l2 0 0 -2 -2 0 0 2zm0 -16l2 0 0 -2 -2 0 0 2zM7 17L17 17 17 7 7 7 7 17Zm2 -8l6 0 0 6 -6 0 0 -6z" />
 | 
				
			||||||
 | 
				
			|||||||
@ -5,5 +5,5 @@
 | 
				
			|||||||
    android:viewportWidth="24">
 | 
					    android:viewportWidth="24">
 | 
				
			||||||
    <path
 | 
					    <path
 | 
				
			||||||
        android:fillColor="?android:attr/colorForeground"
 | 
					        android:fillColor="?android:attr/colorForeground"
 | 
				
			||||||
        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
 | 
					        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
 | 
				
			||||||
</vector>
 | 
					</vector>
 | 
				
			||||||
 | 
				
			|||||||
@ -3,10 +3,14 @@
 | 
				
			|||||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
				
			||||||
    <item>
 | 
					    <item>
 | 
				
			||||||
        <selector>
 | 
					        <selector>
 | 
				
			||||||
            <item app:state_multiselected="true" android:state_activated="true">
 | 
					            <item
 | 
				
			||||||
 | 
					                android:state_activated="true"
 | 
				
			||||||
 | 
					                app:state_multiselected="true">
 | 
				
			||||||
                <color android:color="?attr/colorControlActivated" />
 | 
					                <color android:color="?attr/colorControlActivated" />
 | 
				
			||||||
            </item>
 | 
					            </item>
 | 
				
			||||||
            <item app:state_multiselected="false" android:state_activated="true">
 | 
					            <item
 | 
				
			||||||
 | 
					                android:state_activated="true"
 | 
				
			||||||
 | 
					                app:state_multiselected="false">
 | 
				
			||||||
                <color android:color="?attr/colorControlHighlight" />
 | 
					                <color android:color="?attr/colorControlHighlight" />
 | 
				
			||||||
            </item>
 | 
					            </item>
 | 
				
			||||||
        </selector>
 | 
					        </selector>
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@
 | 
				
			|||||||
    <FrameLayout
 | 
					    <FrameLayout
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
        android:minHeight="200dp" >
 | 
					        android:minHeight="200dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ProgressBar
 | 
					        <ProgressBar
 | 
				
			||||||
            android:id="@+id/progress_bar"
 | 
					            android:id="@+id/progress_bar"
 | 
				
			||||||
@ -28,7 +28,7 @@
 | 
				
			|||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:layout_gravity="center"
 | 
					            android:layout_gravity="center"
 | 
				
			||||||
            android:indeterminate="true"
 | 
					            android:indeterminate="true"
 | 
				
			||||||
            android:visibility="@{appData.isEmpty() ? View.VISIBLE : View.GONE}"/>
 | 
					            android:visibility="@{appData.isEmpty() ? View.VISIBLE : View.GONE}" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <android.support.v7.widget.RecyclerView
 | 
					        <android.support.v7.widget.RecyclerView
 | 
				
			||||||
            android:id="@+id/app_list"
 | 
					            android:id="@+id/app_list"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<layout xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <data>
 | 
					    <data>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,10 +22,10 @@
 | 
				
			|||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:background="@drawable/list_item_background"
 | 
					        android:background="@drawable/list_item_background"
 | 
				
			||||||
        android:padding="16dp"
 | 
					 | 
				
			||||||
        android:orientation="horizontal"
 | 
					 | 
				
			||||||
        android:gravity="center_vertical"
 | 
					        android:gravity="center_vertical"
 | 
				
			||||||
        android:onClick="@{(view) -> item.setExcludedFromTunnel(!item.excludedFromTunnel)}">
 | 
					        android:onClick="@{(view) -> item.setExcludedFromTunnel(!item.excludedFromTunnel)}"
 | 
				
			||||||
 | 
					        android:orientation="horizontal"
 | 
				
			||||||
 | 
					        android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ImageView
 | 
					        <ImageView
 | 
				
			||||||
            android:id="@+id/app_icon"
 | 
					            android:id="@+id/app_icon"
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@
 | 
				
			|||||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <data>
 | 
					    <data>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <import type="com.wireguard.android.widget.NameInputFilter" />
 | 
					        <import type="com.wireguard.android.widget.NameInputFilter" />
 | 
				
			||||||
    </data>
 | 
					    </data>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -100,8 +100,8 @@
 | 
				
			|||||||
                            app:filter="@{KeyInputFilter.newInstance()}" />
 | 
					                            app:filter="@{KeyInputFilter.newInstance()}" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <Button
 | 
					                        <Button
 | 
				
			||||||
                            style="@style/Widget.AppCompat.Button.Borderless.Colored"
 | 
					 | 
				
			||||||
                            android:id="@+id/generate_private_key_button"
 | 
					                            android:id="@+id/generate_private_key_button"
 | 
				
			||||||
 | 
					                            style="@style/Widget.AppCompat.Button.Borderless.Colored"
 | 
				
			||||||
                            android:layout_width="96dp"
 | 
					                            android:layout_width="96dp"
 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
                            android:layout_alignBottom="@id/private_key_text"
 | 
					                            android:layout_alignBottom="@id/private_key_text"
 | 
				
			||||||
@ -217,12 +217,12 @@
 | 
				
			|||||||
                            android:textAlignment="center" />
 | 
					                            android:textAlignment="center" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <Button
 | 
					                        <Button
 | 
				
			||||||
                            style="@style/Widget.AppCompat.Button.Borderless.Colored"
 | 
					 | 
				
			||||||
                            android:id="@+id/set_excluded_applications"
 | 
					                            android:id="@+id/set_excluded_applications"
 | 
				
			||||||
 | 
					                            style="@style/Widget.AppCompat.Button.Borderless.Colored"
 | 
				
			||||||
                            android:layout_width="wrap_content"
 | 
					                            android:layout_width="wrap_content"
 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
                            android:layout_marginLeft="-8dp"
 | 
					 | 
				
			||||||
                            android:layout_below="@+id/dns_servers_text"
 | 
					                            android:layout_below="@+id/dns_servers_text"
 | 
				
			||||||
 | 
					                            android:layout_marginLeft="-8dp"
 | 
				
			||||||
                            android:onClick="@{fragment::onRequestSetExcludedApplications}"
 | 
					                            android:onClick="@{fragment::onRequestSetExcludedApplications}"
 | 
				
			||||||
                            android:text="@{@plurals/set_excluded_applications(config.interfaceSection.excludedApplicationsCount, config.interfaceSection.excludedApplicationsCount)}" />
 | 
					                            android:text="@{@plurals/set_excluded_applications(config.interfaceSection.excludedApplicationsCount, config.interfaceSection.excludedApplicationsCount)}" />
 | 
				
			||||||
                    </RelativeLayout>
 | 
					                    </RelativeLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <data>
 | 
					    <data>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <import type="android.view.View"/>
 | 
					        <import type="android.view.View" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <import type="com.wireguard.android.widget.KeyInputFilter" />
 | 
					        <import type="com.wireguard.android.widget.KeyInputFilter" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <variable
 | 
					        <variable
 | 
				
			||||||
 | 
				
			|||||||
@ -30,65 +30,67 @@
 | 
				
			|||||||
            android:id="@+id/tunnel_list"
 | 
					            android:id="@+id/tunnel_list"
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
            android:layout_height="match_parent"
 | 
					            android:layout_height="match_parent"
 | 
				
			||||||
            android:paddingBottom="@{@dimen/design_fab_size_normal * 1.1f}"
 | 
					 | 
				
			||||||
            android:clipToPadding="false"
 | 
					 | 
				
			||||||
            android:choiceMode="multipleChoiceModal"
 | 
					            android:choiceMode="multipleChoiceModal"
 | 
				
			||||||
 | 
					            android:clipToPadding="false"
 | 
				
			||||||
 | 
					            android:paddingBottom="@{@dimen/design_fab_size_normal * 1.1f}"
 | 
				
			||||||
            android:visibility="@{tunnels.size() > 0 ? android.view.View.VISIBLE : android.view.View.GONE}"
 | 
					            android:visibility="@{tunnels.size() > 0 ? android.view.View.VISIBLE : android.view.View.GONE}"
 | 
				
			||||||
 | 
					            app:configurationHandler="@{rowConfigurationHandler}"
 | 
				
			||||||
            app:items="@{tunnels}"
 | 
					            app:items="@{tunnels}"
 | 
				
			||||||
            app:layout="@{@layout/tunnel_list_item}"
 | 
					            app:layout="@{@layout/tunnel_list_item}" />
 | 
				
			||||||
            app:configurationHandler="@{rowConfigurationHandler}" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_gravity="center"
 | 
				
			||||||
            android:orientation="vertical"
 | 
					            android:orientation="vertical"
 | 
				
			||||||
            android:visibility="@{tunnels.size() == 0 ? android.view.View.VISIBLE : android.view.View.GONE}"
 | 
					            android:visibility="@{tunnels.size() == 0 ? android.view.View.VISIBLE : android.view.View.GONE}">
 | 
				
			||||||
            android:layout_gravity="center">
 | 
					
 | 
				
			||||||
            <android.support.v7.widget.AppCompatImageView
 | 
					            <android.support.v7.widget.AppCompatImageView
 | 
				
			||||||
                android:id="@+id/logo_placeholder"
 | 
					                android:id="@+id/logo_placeholder"
 | 
				
			||||||
                android:layout_width="140dp"
 | 
					                android:layout_width="140dp"
 | 
				
			||||||
                android:layout_height="140dp"
 | 
					                android:layout_height="140dp"
 | 
				
			||||||
                android:layout_gravity="center"
 | 
					                android:layout_gravity="center"
 | 
				
			||||||
                android:alpha="0.3333333"
 | 
					 | 
				
			||||||
                android:layout_marginTop="-70dp"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="20dp"
 | 
					                android:layout_marginBottom="20dp"
 | 
				
			||||||
 | 
					                android:layout_marginTop="-70dp"
 | 
				
			||||||
 | 
					                android:alpha="0.3333333"
 | 
				
			||||||
                android:src="@mipmap/ic_launcher" />
 | 
					                android:src="@mipmap/ic_launcher" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextView
 | 
					            <TextView
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:layout_gravity="center"
 | 
					                android:layout_gravity="center"
 | 
				
			||||||
                android:textSize="20sp"
 | 
					                android:text="@string/tunnel_list_placeholder"
 | 
				
			||||||
                android:text="@string/tunnel_list_placeholder" />
 | 
					                android:textSize="20sp" />
 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <com.wireguard.android.widget.fab.FloatingActionsMenu
 | 
					        <com.wireguard.android.widget.fab.FloatingActionsMenu
 | 
				
			||||||
            android:id="@+id/create_menu"
 | 
					            android:id="@+id/create_menu"
 | 
				
			||||||
            android:clipChildren="false"
 | 
					 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:layout_gravity="bottom|end"
 | 
					            android:layout_gravity="bottom|end"
 | 
				
			||||||
            android:layout_margin="@dimen/fab_margin"
 | 
					            android:layout_margin="@dimen/fab_margin"
 | 
				
			||||||
 | 
					            android:clipChildren="false"
 | 
				
			||||||
            app:fab_labelStyle="@style/fab_label"
 | 
					            app:fab_labelStyle="@style/fab_label"
 | 
				
			||||||
            app:fab_labelsPosition="@integer/label_position"
 | 
					            app:fab_labelsPosition="@integer/label_position"
 | 
				
			||||||
            app:layout_behavior="com.wireguard.android.widget.fab.FloatingActionButtonBehavior" >
 | 
					            app:layout_behavior="com.wireguard.android.widget.fab.FloatingActionButtonBehavior">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
					            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
				
			||||||
                android:id="@+id/create_from_file"
 | 
					                android:id="@+id/create_from_file"
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:onClick="@{fragment::onRequestImportConfig}"
 | 
					                android:onClick="@{fragment::onRequestImportConfig}"
 | 
				
			||||||
                app:srcCompat="@drawable/ic_action_open_white"
 | 
					 | 
				
			||||||
                app:fabSize="mini"
 | 
					                app:fabSize="mini"
 | 
				
			||||||
                app:fab_title="@string/create_from_file" />
 | 
					                app:fab_title="@string/create_from_file"
 | 
				
			||||||
 | 
					                app:srcCompat="@drawable/ic_action_open_white" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
					            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
				
			||||||
                android:id="@+id/scan_qr_code"
 | 
					                android:id="@+id/scan_qr_code"
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:onClick="@{fragment::onRequestScanQRCode}"
 | 
					                android:onClick="@{fragment::onRequestScanQRCode}"
 | 
				
			||||||
                app:srcCompat="@drawable/ic_action_scan_qr_code_white"
 | 
					 | 
				
			||||||
                app:fabSize="mini"
 | 
					                app:fabSize="mini"
 | 
				
			||||||
                app:fab_title="@string/create_from_qrcode" />
 | 
					                app:fab_title="@string/create_from_qrcode"
 | 
				
			||||||
 | 
					                app:srcCompat="@drawable/ic_action_scan_qr_code_white" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
					            <com.wireguard.android.widget.fab.LabeledFloatingActionButton
 | 
				
			||||||
                android:id="@+id/create_empty"
 | 
					                android:id="@+id/create_empty"
 | 
				
			||||||
@ -96,8 +98,8 @@
 | 
				
			|||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:onClick="@{fragment::onRequestCreateConfig}"
 | 
					                android:onClick="@{fragment::onRequestCreateConfig}"
 | 
				
			||||||
                app:fabSize="mini"
 | 
					                app:fabSize="mini"
 | 
				
			||||||
                app:srcCompat="@drawable/ic_action_edit_white"
 | 
					                app:fab_title="@string/create_empty"
 | 
				
			||||||
                app:fab_title="@string/create_empty" />
 | 
					                app:srcCompat="@drawable/ic_action_edit_white" />
 | 
				
			||||||
        </com.wireguard.android.widget.fab.FloatingActionsMenu>
 | 
					        </com.wireguard.android.widget.fab.FloatingActionsMenu>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </android.support.design.widget.CoordinatorLayout>
 | 
					    </android.support.design.widget.CoordinatorLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
        android:id="@+id/menu_settings"
 | 
					        android:id="@+id/menu_settings"
 | 
				
			||||||
        android:alphabeticShortcut="s"
 | 
					        android:alphabeticShortcut="s"
 | 
				
			||||||
        android:icon="@drawable/ic_settings"
 | 
					        android:icon="@drawable/ic_settings"
 | 
				
			||||||
 | 
					        android:orderInCategory="1000"
 | 
				
			||||||
        android:title="@string/settings"
 | 
					        android:title="@string/settings"
 | 
				
			||||||
        app:showAsAction="always"
 | 
					        app:showAsAction="always" />
 | 
				
			||||||
        android:orderInCategory="1000" />
 | 
					 | 
				
			||||||
</menu>
 | 
					</menu>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <declare-styleable name="Multiselected">
 | 
					    <declare-styleable name="Multiselected">
 | 
				
			||||||
        <attr name="state_multiselected" format="boolean"/>
 | 
					        <attr name="state_multiselected" format="boolean" />
 | 
				
			||||||
    </declare-styleable>
 | 
					    </declare-styleable>
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <item name="fab_expand_menu_button" type="id"/>
 | 
					    <item name="fab_expand_menu_button" type="id" />
 | 
				
			||||||
    <item name="fab_label" type="id"/>
 | 
					    <item name="fab_label" type="id" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <dimen name="fab_shadow_offset">3dp</dimen>
 | 
					    <dimen name="fab_shadow_offset">3dp</dimen>
 | 
				
			||||||
    <dimen name="fab_shadow_radius">9dp</dimen>
 | 
					    <dimen name="fab_shadow_radius">9dp</dimen>
 | 
				
			||||||
@ -12,19 +12,19 @@
 | 
				
			|||||||
    <dimen name="fab_labels_margin">8dp</dimen>
 | 
					    <dimen name="fab_labels_margin">8dp</dimen>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <declare-styleable name="LabeledFloatingActionButton">
 | 
					    <declare-styleable name="LabeledFloatingActionButton">
 | 
				
			||||||
        <attr name="fab_title" format="string"/>
 | 
					        <attr name="fab_title" format="string" />
 | 
				
			||||||
    </declare-styleable>
 | 
					    </declare-styleable>
 | 
				
			||||||
    <declare-styleable name="FloatingActionsMenu">
 | 
					    <declare-styleable name="FloatingActionsMenu">
 | 
				
			||||||
        <attr name="fab_labelStyle" format="reference"/>
 | 
					        <attr name="fab_labelStyle" format="reference" />
 | 
				
			||||||
        <attr name="fab_labelsPosition" format="enum">
 | 
					        <attr name="fab_labelsPosition" format="enum">
 | 
				
			||||||
            <enum name="left" value="0"/>
 | 
					            <enum name="left" value="0" />
 | 
				
			||||||
            <enum name="right" value="1"/>
 | 
					            <enum name="right" value="1" />
 | 
				
			||||||
        </attr>
 | 
					        </attr>
 | 
				
			||||||
        <attr name="fab_expandDirection" format="enum">
 | 
					        <attr name="fab_expandDirection" format="enum">
 | 
				
			||||||
            <enum name="up" value="0"/>
 | 
					            <enum name="up" value="0" />
 | 
				
			||||||
            <enum name="down" value="1"/>
 | 
					            <enum name="down" value="1" />
 | 
				
			||||||
            <enum name="left" value="2"/>
 | 
					            <enum name="left" value="2" />
 | 
				
			||||||
            <enum name="right" value="3"/>
 | 
					            <enum name="right" value="3" />
 | 
				
			||||||
        </attr>
 | 
					        </attr>
 | 
				
			||||||
    </declare-styleable>
 | 
					    </declare-styleable>
 | 
				
			||||||
    <integer name="label_position">0</integer>
 | 
					    <integer name="label_position">0</integer>
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@
 | 
				
			|||||||
    <CheckBoxPreference
 | 
					    <CheckBoxPreference
 | 
				
			||||||
        android:defaultValue="false"
 | 
					        android:defaultValue="false"
 | 
				
			||||||
        android:key="dark_theme"
 | 
					        android:key="dark_theme"
 | 
				
			||||||
        android:summaryOn="@string/dark_theme_summary_on"
 | 
					 | 
				
			||||||
        android:summaryOff="@string/dark_theme_summary_off"
 | 
					        android:summaryOff="@string/dark_theme_summary_off"
 | 
				
			||||||
 | 
					        android:summaryOn="@string/dark_theme_summary_on"
 | 
				
			||||||
        android:title="@string/dark_theme_title" />
 | 
					        android:title="@string/dark_theme_title" />
 | 
				
			||||||
</PreferenceScreen>
 | 
					</PreferenceScreen>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user