Convert activity package to Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
85aa5fbd46
commit
04d0b819f6
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.model.ObservableTunnel;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.CallbackRegistry;
|
||||
import androidx.databinding.CallbackRegistry.NotifierCallback;
|
||||
|
||||
/**
|
||||
* Base class for activities that need to remember the currently-selected tunnel.
|
||||
*/
|
||||
|
||||
@NonNullForAll
|
||||
public abstract class BaseActivity extends ThemeChangeAwareActivity {
|
||||
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
|
||||
|
||||
private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
|
||||
@Nullable private ObservableTunnel selectedTunnel;
|
||||
|
||||
public void addOnSelectedTunnelChangedListener(final OnSelectedTunnelChangedListener listener) {
|
||||
selectionChangeRegistry.add(listener);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ObservableTunnel getSelectedTunnel() {
|
||||
return selectedTunnel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
|
||||
final String savedTunnelName;
|
||||
if (savedInstanceState != null)
|
||||
savedTunnelName = savedInstanceState.getString(KEY_SELECTED_TUNNEL);
|
||||
else if (getIntent() != null)
|
||||
savedTunnelName = getIntent().getStringExtra(KEY_SELECTED_TUNNEL);
|
||||
else
|
||||
savedTunnelName = null;
|
||||
|
||||
if (savedTunnelName != null)
|
||||
Application.getTunnelManager().getTunnels()
|
||||
.thenAccept(tunnels -> setSelectedTunnel(tunnels.get(savedTunnelName)));
|
||||
|
||||
// The selected tunnel must be set before the superclass method recreates fragments.
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(final Bundle outState) {
|
||||
if (selectedTunnel != null)
|
||||
outState.putString(KEY_SELECTED_TUNNEL, selectedTunnel.getName());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
protected abstract void onSelectedTunnelChanged(@Nullable ObservableTunnel oldTunnel, @Nullable ObservableTunnel newTunnel);
|
||||
|
||||
public void removeOnSelectedTunnelChangedListener(
|
||||
final OnSelectedTunnelChangedListener listener) {
|
||||
selectionChangeRegistry.remove(listener);
|
||||
}
|
||||
|
||||
public void setSelectedTunnel(@Nullable final ObservableTunnel tunnel) {
|
||||
final ObservableTunnel oldTunnel = selectedTunnel;
|
||||
if (Objects.equals(oldTunnel, tunnel))
|
||||
return;
|
||||
selectedTunnel = tunnel;
|
||||
onSelectedTunnelChanged(oldTunnel, tunnel);
|
||||
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, tunnel);
|
||||
}
|
||||
|
||||
public interface OnSelectedTunnelChangedListener {
|
||||
void onSelectedTunnelChanged(@Nullable ObservableTunnel oldTunnel, @Nullable ObservableTunnel newTunnel);
|
||||
}
|
||||
|
||||
private static final class SelectionChangeNotifier
|
||||
extends NotifierCallback<OnSelectedTunnelChangedListener, ObservableTunnel, ObservableTunnel> {
|
||||
@Override
|
||||
public void onNotifyCallback(final OnSelectedTunnelChangedListener listener,
|
||||
final ObservableTunnel oldTunnel, final int ignored,
|
||||
final ObservableTunnel newTunnel) {
|
||||
listener.onSelectedTunnelChanged(oldTunnel, newTunnel);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SelectionChangeRegistry
|
||||
extends CallbackRegistry<OnSelectedTunnelChangedListener, ObservableTunnel, ObservableTunnel> {
|
||||
private SelectionChangeRegistry() {
|
||||
super(new SelectionChangeNotifier());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.databinding.CallbackRegistry
|
||||
import androidx.databinding.CallbackRegistry.NotifierCallback
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
|
||||
/**
|
||||
* Base class for activities that need to remember the currently-selected tunnel.
|
||||
*/
|
||||
abstract class BaseActivity : ThemeChangeAwareActivity() {
|
||||
private val selectionChangeRegistry = SelectionChangeRegistry()
|
||||
var selectedTunnel: ObservableTunnel? = null
|
||||
set(value) {
|
||||
val oldTunnel = field
|
||||
if (oldTunnel == value) return
|
||||
field = value
|
||||
onSelectedTunnelChanged(oldTunnel, value)
|
||||
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, value)
|
||||
}
|
||||
fun addOnSelectedTunnelChangedListener(listener: OnSelectedTunnelChangedListener) {
|
||||
selectionChangeRegistry.add(listener)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
|
||||
val savedTunnelName = when {
|
||||
savedInstanceState != null -> savedInstanceState.getString(KEY_SELECTED_TUNNEL)
|
||||
intent != null -> intent.getStringExtra(KEY_SELECTED_TUNNEL)
|
||||
else -> null
|
||||
}
|
||||
if (savedTunnelName != null) {
|
||||
Application.getTunnelManager()
|
||||
.tunnels
|
||||
.thenAccept { selectedTunnel = it[savedTunnelName] }
|
||||
}
|
||||
|
||||
// The selected tunnel must be set before the superclass method recreates fragments.
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
if (selectedTunnel != null) outState.putString(KEY_SELECTED_TUNNEL, selectedTunnel!!.name)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
protected abstract fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?)
|
||||
fun removeOnSelectedTunnelChangedListener(
|
||||
listener: OnSelectedTunnelChangedListener) {
|
||||
selectionChangeRegistry.remove(listener)
|
||||
}
|
||||
|
||||
interface OnSelectedTunnelChangedListener {
|
||||
fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?)
|
||||
}
|
||||
|
||||
private class SelectionChangeNotifier : NotifierCallback<OnSelectedTunnelChangedListener, ObservableTunnel, ObservableTunnel>() {
|
||||
override fun onNotifyCallback(
|
||||
listener: OnSelectedTunnelChangedListener,
|
||||
oldTunnel: ObservableTunnel?,
|
||||
ignored: Int,
|
||||
newTunnel: ObservableTunnel?
|
||||
) {
|
||||
listener.onSelectedTunnelChanged(oldTunnel, newTunnel)
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectionChangeRegistry :
|
||||
CallbackRegistry<OnSelectedTunnelChangedListener, ObservableTunnel, ObservableTunnel>(SelectionChangeNotifier())
|
||||
|
||||
companion object {
|
||||
private const val KEY_SELECTED_TUNNEL = "selected_tunnel"
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.fragment.TunnelDetailFragment;
|
||||
import com.wireguard.android.fragment.TunnelEditorFragment;
|
||||
import com.wireguard.android.model.ObservableTunnel;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
/**
|
||||
* CRUD interface for WireGuard tunnels. This activity serves as the main entry point to the
|
||||
* WireGuard application, and contains several fragments for listing, viewing details of, and
|
||||
* editing the configuration and interface state of WireGuard tunnels.
|
||||
*/
|
||||
|
||||
@NonNullForAll
|
||||
public class MainActivity extends BaseActivity
|
||||
implements FragmentManager.OnBackStackChangedListener {
|
||||
@Nullable private ActionBar actionBar;
|
||||
private boolean isTwoPaneLayout;
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
final int backStackEntries = getSupportFragmentManager().getBackStackEntryCount();
|
||||
// If the two-pane layout does not have an editor open, going back should exit the app.
|
||||
if (isTwoPaneLayout && backStackEntries <= 1) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
|
||||
if (!isTwoPaneLayout && backStackEntries == 1) {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
setSelectedTunnel(null);
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override public void onBackStackChanged() {
|
||||
if (actionBar == null)
|
||||
return;
|
||||
// Do not show the home menu when the two-pane layout is at the detail view (see above).
|
||||
final int backStackEntries = getSupportFragmentManager().getBackStackEntryCount();
|
||||
final int minBackStackEntries = isTwoPaneLayout ? 2 : 1;
|
||||
actionBar.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries);
|
||||
}
|
||||
|
||||
// We use onTouchListener here to avoid the UI click sound, hence
|
||||
// calling View#performClick defeats the purpose of it.
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main_activity);
|
||||
actionBar = getSupportActionBar();
|
||||
isTwoPaneLayout = findViewById(R.id.master_detail_wrapper) instanceof LinearLayout;
|
||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||
onBackStackChanged();
|
||||
// Dispatch insets on back stack change
|
||||
// This is required to ensure replaced fragments are also able to consume insets
|
||||
findViewById(R.id.master_detail_wrapper).setOnApplyWindowInsetsListener((v, insets) -> {
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
fragmentManager.addOnBackStackChangedListener(() -> {
|
||||
final List<Fragment> fragments = fragmentManager.getFragments();
|
||||
for (int i = 0; i < fragments.size(); i++) {
|
||||
fragments.get(i).requireView().dispatchApplyWindowInsets(insets);
|
||||
}
|
||||
});
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.main_activity, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("UnnecessaryFullyQualifiedName")
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
// The back arrow in the action bar should act the same as the back button.
|
||||
onBackPressed();
|
||||
return true;
|
||||
case R.id.menu_action_edit:
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.detail_container, new TunnelEditorFragment())
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
return true;
|
||||
case R.id.menu_action_save:
|
||||
// This menu item is handled by the editor fragment.
|
||||
return false;
|
||||
case R.id.menu_settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelectedTunnelChanged(@Nullable final ObservableTunnel oldTunnel,
|
||||
@Nullable final ObservableTunnel newTunnel) {
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
final int backStackEntries = fragmentManager.getBackStackEntryCount();
|
||||
if (newTunnel == null) {
|
||||
// Clear everything off the back stack (all editors and detail fragments).
|
||||
fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
return;
|
||||
}
|
||||
if (backStackEntries == 2) {
|
||||
// Pop the editor off the back stack to reveal the detail fragment. Use the immediate
|
||||
// method to avoid the editor picking up the new tunnel while it is still visible.
|
||||
fragmentManager.popBackStackImmediate();
|
||||
} else if (backStackEntries == 0) {
|
||||
// Create and show a new detail fragment.
|
||||
fragmentManager.beginTransaction()
|
||||
.add(R.id.detail_container, new TunnelDetailFragment())
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
126
ui/src/main/java/com/wireguard/android/activity/MainActivity.kt
Normal file
126
ui/src/main/java/com/wireguard/android/activity/MainActivity.kt
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.ActionBar
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.fragment.TunnelDetailFragment
|
||||
import com.wireguard.android.fragment.TunnelEditorFragment
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
|
||||
/**
|
||||
* CRUD interface for WireGuard tunnels. This activity serves as the main entry point to the
|
||||
* WireGuard application, and contains several fragments for listing, viewing details of, and
|
||||
* editing the configuration and interface state of WireGuard tunnels.
|
||||
*/
|
||||
class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
|
||||
private var actionBar: ActionBar? = null
|
||||
private var isTwoPaneLayout = false
|
||||
|
||||
override fun onBackPressed() {
|
||||
val backStackEntries = supportFragmentManager.backStackEntryCount
|
||||
// If the two-pane layout does not have an editor open, going back should exit the app.
|
||||
if (isTwoPaneLayout && backStackEntries <= 1) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
|
||||
if (!isTwoPaneLayout && backStackEntries == 1) {
|
||||
supportFragmentManager.popBackStack()
|
||||
selectedTunnel = null
|
||||
return
|
||||
}
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackStackChanged() {
|
||||
if (actionBar == null) return
|
||||
// Do not show the home menu when the two-pane layout is at the detail view (see above).
|
||||
val backStackEntries = supportFragmentManager.backStackEntryCount
|
||||
val minBackStackEntries = if (isTwoPaneLayout) 2 else 1
|
||||
actionBar!!.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.main_activity)
|
||||
actionBar = supportActionBar
|
||||
isTwoPaneLayout = findViewById<View>(R.id.master_detail_wrapper) is LinearLayout
|
||||
supportFragmentManager.addOnBackStackChangedListener(this)
|
||||
onBackStackChanged()
|
||||
// Dispatch insets on back stack change
|
||||
// This is required to ensure replaced fragments are also able to consume insets
|
||||
findViewById<View>(R.id.master_detail_wrapper).setOnApplyWindowInsetsListener { _, insets ->
|
||||
val fragmentManager = supportFragmentManager
|
||||
fragmentManager.addOnBackStackChangedListener {
|
||||
fragmentManager.fragments.forEach {
|
||||
it.requireView().dispatchApplyWindowInsets(insets)
|
||||
}
|
||||
}
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.main_activity, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
// The back arrow in the action bar should act the same as the back button.
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
R.id.menu_action_edit -> {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.detail_container, TunnelEditorFragment())
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true
|
||||
}
|
||||
// This menu item is handled by the editor fragment.
|
||||
R.id.menu_action_save -> false
|
||||
R.id.menu_settings -> {
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?,
|
||||
newTunnel: ObservableTunnel?) {
|
||||
val fragmentManager = supportFragmentManager
|
||||
val backStackEntries = fragmentManager.backStackEntryCount
|
||||
if (newTunnel == null) {
|
||||
// Clear everything off the back stack (all editors and detail fragments).
|
||||
fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
return
|
||||
}
|
||||
if (backStackEntries == 2) {
|
||||
// Pop the editor off the back stack to reveal the detail fragment. Use the immediate
|
||||
// method to avoid the editor picking up the new tunnel while it is still visible.
|
||||
fragmentManager.popBackStackImmediate()
|
||||
} else if (backStackEntries == 0) {
|
||||
// Create and show a new detail fragment.
|
||||
fragmentManager.beginTransaction()
|
||||
.add(R.id.detail_container, TunnelDetailFragment())
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.backend.WgQuickBackend;
|
||||
import com.wireguard.android.util.ModuleLoader;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
/**
|
||||
* Interface for changing application-global persistent settings.
|
||||
*/
|
||||
|
||||
@NonNullForAll
|
||||
public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||
private final SparseArray<PermissionRequestCallback> permissionRequestCallbacks = new SparseArray<>();
|
||||
private int permissionRequestCounter;
|
||||
|
||||
public void ensurePermissions(final String[] permissions, final PermissionRequestCallback cb) {
|
||||
final List<String> needPermissions = new ArrayList<>(permissions.length);
|
||||
for (final String permission : permissions) {
|
||||
if (ContextCompat.checkSelfPermission(this, permission)
|
||||
!= PackageManager.PERMISSION_GRANTED)
|
||||
needPermissions.add(permission);
|
||||
}
|
||||
if (needPermissions.isEmpty()) {
|
||||
final int[] granted = new int[permissions.length];
|
||||
Arrays.fill(granted, PackageManager.PERMISSION_GRANTED);
|
||||
cb.done(permissions, granted);
|
||||
return;
|
||||
}
|
||||
final int idx = permissionRequestCounter++;
|
||||
permissionRequestCallbacks.put(idx, cb);
|
||||
ActivityCompat.requestPermissions(this,
|
||||
needPermissions.toArray(new String[needPermissions.size()]), idx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(android.R.id.content, new SettingsFragment())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("UnnecessaryFullyQualifiedName")
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode,
|
||||
final String[] permissions,
|
||||
final int[] grantResults) {
|
||||
final PermissionRequestCallback f = permissionRequestCallbacks.get(requestCode);
|
||||
if (f != null) {
|
||||
permissionRequestCallbacks.remove(requestCode);
|
||||
f.done(permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
public interface PermissionRequestCallback {
|
||||
void done(String[] permissions, int[] grantResults);
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||
@Override
|
||||
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
screen.removePreference(getPreferenceManager().findPreference("dark_theme"));
|
||||
|
||||
final Preference[] wgQuickOnlyPrefs = {
|
||||
getPreferenceManager().findPreference("tools_installer"),
|
||||
getPreferenceManager().findPreference("restore_on_boot"),
|
||||
getPreferenceManager().findPreference("multiple_tunnels")
|
||||
};
|
||||
for (final Preference pref : wgQuickOnlyPrefs)
|
||||
pref.setVisible(false);
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
for (final Preference pref : wgQuickOnlyPrefs) {
|
||||
if (backend instanceof WgQuickBackend)
|
||||
pref.setVisible(true);
|
||||
else
|
||||
screen.removePreference(pref);
|
||||
}
|
||||
});
|
||||
|
||||
final Preference moduleInstaller = getPreferenceManager().findPreference("module_downloader");
|
||||
final Preference kernelModuleDisabler = getPreferenceManager().findPreference("kernel_module_disabler");
|
||||
moduleInstaller.setVisible(false);
|
||||
if (ModuleLoader.isModuleLoaded()) {
|
||||
screen.removePreference(moduleInstaller);
|
||||
} else {
|
||||
screen.removePreference(kernelModuleDisabler);
|
||||
Application.getAsyncWorker().runAsync(Application.getRootShell()::start).whenComplete((v, e) -> {
|
||||
if (e == null)
|
||||
moduleInstaller.setVisible(true);
|
||||
else
|
||||
screen.removePreference(moduleInstaller);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.SparseArray
|
||||
import android.view.MenuItem
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.Backend
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.wireguard.android.util.ModuleLoader
|
||||
import java.util.ArrayList
|
||||
import java.util.Arrays
|
||||
|
||||
/**
|
||||
* Interface for changing application-global persistent settings.
|
||||
*/
|
||||
class SettingsActivity : ThemeChangeAwareActivity() {
|
||||
private val permissionRequestCallbacks = SparseArray<PermissionRequestCallback>()
|
||||
private var permissionRequestCounter = 0
|
||||
|
||||
fun ensurePermissions(permissions: Array<String>, cb: PermissionRequestCallback) {
|
||||
val needPermissions: MutableList<String> = ArrayList(permissions.size)
|
||||
permissions.forEach {
|
||||
if (ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED) {
|
||||
needPermissions.add(it)
|
||||
}
|
||||
}
|
||||
if (needPermissions.isEmpty()) {
|
||||
val granted = IntArray(permissions.size)
|
||||
Arrays.fill(granted, PackageManager.PERMISSION_GRANTED)
|
||||
cb.done(permissions, granted)
|
||||
return
|
||||
}
|
||||
val idx = permissionRequestCounter++
|
||||
permissionRequestCallbacks.put(idx, cb)
|
||||
ActivityCompat.requestPermissions(this,
|
||||
needPermissions.toTypedArray(), idx)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (supportFragmentManager.findFragmentById(android.R.id.content) == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(android.R.id.content, SettingsFragment())
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray) {
|
||||
val f = permissionRequestCallbacks[requestCode]
|
||||
if (f != null) {
|
||||
permissionRequestCallbacks.remove(requestCode)
|
||||
f.done(permissions, grantResults)
|
||||
}
|
||||
}
|
||||
|
||||
interface PermissionRequestCallback {
|
||||
fun done(permissions: Array<String>, grantResults: IntArray)
|
||||
}
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, key: String?) {
|
||||
addPreferencesFromResource(R.xml.preferences)
|
||||
val screen = preferenceScreen
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) screen.removePreference(preferenceManager.findPreference("dark_theme"))
|
||||
val wgQuickOnlyPrefs = arrayOf(
|
||||
preferenceManager.findPreference("tools_installer"),
|
||||
preferenceManager.findPreference("restore_on_boot"),
|
||||
preferenceManager.findPreference<Preference>("multiple_tunnels")
|
||||
).filterNotNull()
|
||||
wgQuickOnlyPrefs.forEach { it.isVisible = false }
|
||||
Application.getBackendAsync().thenAccept { backend ->
|
||||
if (backend is WgQuickBackend) {
|
||||
wgQuickOnlyPrefs.forEach { it.isVisible = true }
|
||||
} else {
|
||||
wgQuickOnlyPrefs.forEach { screen.removePreference(it) }
|
||||
}
|
||||
}
|
||||
val moduleInstaller = preferenceManager.findPreference<Preference>("module_downloader")
|
||||
val kernelModuleDisabler = preferenceManager.findPreference<Preference>("kernel_module_disabler")
|
||||
moduleInstaller?.isVisible = false
|
||||
if (ModuleLoader.isModuleLoaded()) {
|
||||
screen.removePreference(moduleInstaller)
|
||||
} else {
|
||||
screen.removePreference(kernelModuleDisabler)
|
||||
Application.getAsyncWorker().runAsync(Application.getRootShell()::start).whenComplete { _, e ->
|
||||
if (e == null)
|
||||
moduleInstaller?.isVisible = true
|
||||
else
|
||||
screen.removePreference(moduleInstaller)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
@NonNullForAll
|
||||
public abstract class ThemeChangeAwareActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final String TAG = "WireGuard/" + ThemeChangeAwareActivity.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
Application.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
|
||||
if ("dark_theme".equals(key)) {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
sharedPreferences.getBoolean(key, false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES :
|
||||
AppCompatDelegate.MODE_NIGHT_NO);
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.wireguard.android.Application
|
||||
|
||||
abstract class ThemeChangeAwareActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
Application.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
when (key) {
|
||||
"dark_theme" -> {
|
||||
AppCompatDelegate.setDefaultNightMode(if (sharedPreferences.getBoolean(key, false)) {
|
||||
AppCompatDelegate.MODE_NIGHT_YES
|
||||
} else {
|
||||
AppCompatDelegate.MODE_NIGHT_NO
|
||||
})
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.wireguard.android.fragment.TunnelEditorFragment;
|
||||
import com.wireguard.android.model.ObservableTunnel;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Standalone activity for creating tunnels.
|
||||
*/
|
||||
|
||||
@NonNullForAll
|
||||
public class TunnelCreatorActivity extends BaseActivity {
|
||||
@Override
|
||||
@SuppressWarnings("UnnecessaryFullyQualifiedName")
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(android.R.id.content, new TunnelEditorFragment())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelectedTunnelChanged(@Nullable final ObservableTunnel oldTunnel, @Nullable final ObservableTunnel newTunnel) {
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import com.wireguard.android.fragment.TunnelEditorFragment
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
|
||||
/**
|
||||
* Standalone activity for creating tunnels.
|
||||
*/
|
||||
class TunnelCreatorActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (supportFragmentManager.findFragmentById(android.R.id.content) == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(android.R.id.content, TunnelEditorFragment())
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
|
||||
finish()
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.QuickTileService;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.backend.Tunnel.State;
|
||||
import com.wireguard.android.model.ObservableTunnel;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@NonNullForAll
|
||||
public class TunnelToggleActivity extends AppCompatActivity {
|
||||
private static final String TAG = "WireGuard/" + TunnelToggleActivity.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final ObservableTunnel tunnel = Application.getTunnelManager().getLastUsedTunnel();
|
||||
if (tunnel == null)
|
||||
return;
|
||||
tunnel.setState(State.TOGGLE).whenComplete((v, t) -> {
|
||||
TileService.requestListeningState(this, new ComponentName(this, QuickTileService.class));
|
||||
onToggleFinished(t);
|
||||
finishAffinity();
|
||||
});
|
||||
}
|
||||
|
||||
private void onToggleFinished(@Nullable final Throwable throwable) {
|
||||
if (throwable == null)
|
||||
return;
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final String message = getString(R.string.toggle_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.service.quicksettings.TileService
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.QuickTileService
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
class TunnelToggleActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val tunnel = Application.getTunnelManager().lastUsedTunnel ?: return
|
||||
tunnel.setState(Tunnel.State.TOGGLE).whenComplete { _, t ->
|
||||
TileService.requestListeningState(this, ComponentName(this, QuickTileService::class.java))
|
||||
onToggleFinished(t)
|
||||
finishAffinity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onToggleFinished(throwable: Throwable?) {
|
||||
if (throwable == null) return
|
||||
val error = ErrorMessages.get(throwable)
|
||||
val message = getString(R.string.toggle_error, error)
|
||||
Log.e(TAG, message, throwable)
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/" + TunnelToggleActivity::class.java.simpleName
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ import androidx.preference.Preference
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.activity.SettingsActivity
|
||||
import com.wireguard.android.util.DownloadsFileSaver
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.util.FragmentUtils
|
||||
@ -80,12 +81,16 @@ class LogExporterPreference(context: Context, attrs: AttributeSet?) : Preference
|
||||
override fun getTitle() = context.getString(R.string.log_export_title)
|
||||
|
||||
override fun onClick() {
|
||||
FragmentUtils.getPrefActivity(this).ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, granted ->
|
||||
if (granted.isNotEmpty() && granted[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportLog()
|
||||
}
|
||||
}
|
||||
FragmentUtils.getPrefActivity(this)
|
||||
.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
object: SettingsActivity.PermissionRequestCallback {
|
||||
override fun done(permissions: Array<String>, grantResults: IntArray) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportLog()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -13,6 +13,7 @@ import androidx.preference.Preference
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.activity.SettingsActivity
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
import com.wireguard.android.util.DownloadsFileSaver
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
@ -84,12 +85,16 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
|
||||
override fun getTitle() = context.getString(R.string.zip_export_title)
|
||||
|
||||
override fun onClick() {
|
||||
FragmentUtils.getPrefActivity(this).ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, granted ->
|
||||
if (granted.isNotEmpty() && granted[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportZip()
|
||||
}
|
||||
}
|
||||
FragmentUtils.getPrefActivity(this)
|
||||
.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
object : SettingsActivity.PermissionRequestCallback {
|
||||
override fun done(permissions: Array<String>, grantResults: IntArray) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportZip()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
Loading…
Reference in New Issue
Block a user