瀏覽代碼

ui: root: rewrite in kotlin

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
master
Jason A. Donenfeld 4 年之前
父節點
當前提交
85dd303c88
共有 7 個文件被更改,包括 352 次插入398 次删除
  1. +0
    -180
      ui/src/main/java/com/wireguard/android/Application.java
  2. +159
    -0
      ui/src/main/java/com/wireguard/android/Application.kt
  3. +0
    -40
      ui/src/main/java/com/wireguard/android/BootShutdownReceiver.java
  4. +34
    -0
      ui/src/main/java/com/wireguard/android/BootShutdownReceiver.kt
  5. +0
    -177
      ui/src/main/java/com/wireguard/android/QuickTileService.java
  6. +158
    -0
      ui/src/main/java/com/wireguard/android/QuickTileService.kt
  7. +1
    -1
      ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt

+ 0
- 180
ui/src/main/java/com/wireguard/android/Application.java 查看文件

@@ -1,180 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.StrictMode;
import android.util.Log;

import com.wireguard.android.backend.Backend;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.backend.WgQuickBackend;
import com.wireguard.android.configStore.FileConfigStore;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.AsyncWorker;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.util.ModuleLoader;
import com.wireguard.android.util.RootShell;
import com.wireguard.android.util.ToolsInstaller;
import com.wireguard.util.NonNullForAll;

import java.lang.ref.WeakReference;
import java.util.Locale;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import java9.util.concurrent.CompletableFuture;

@NonNullForAll
public class Application extends android.app.Application implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, Build.SUPPORTED_ABIS.length > 0 ? Build.SUPPORTED_ABIS[0] : "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT);
private static final String TAG = "WireGuard/" + Application.class.getSimpleName();
@SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
@SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
@Nullable private Backend backend;
@SuppressWarnings("NullableProblems") private ModuleLoader moduleLoader;
@SuppressWarnings("NullableProblems") private RootShell rootShell;
@SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
@SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
@SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;

public Application() {
weakSelf = new WeakReference<>(this);
}

public static Application get() {
return weakSelf.get();
}

public static AsyncWorker getAsyncWorker() {
return get().asyncWorker;
}

public static Backend getBackend() {
final Application app = get();
synchronized (app.futureBackend) {
if (app.backend == null) {
Backend backend = null;
boolean didStartRootShell = false;
if (!ModuleLoader.isModuleLoaded() && app.moduleLoader.moduleMightExist()) {
try {
app.rootShell.start();
didStartRootShell = true;
app.moduleLoader.loadModule();
} catch (final Exception ignored) {
}
}
if (!app.sharedPreferences.getBoolean("disable_kernel_module", false) && ModuleLoader.isModuleLoaded()) {
try {
if (!didStartRootShell)
app.rootShell.start();
final WgQuickBackend wgQuickBackend = new WgQuickBackend(app.getApplicationContext(), app.rootShell, app.toolsInstaller);
wgQuickBackend.setMultipleTunnels(app.sharedPreferences.getBoolean("multiple_tunnels", false));
backend = wgQuickBackend;
} catch (final Exception ignored) {
}
}
if (backend == null) {
backend = new GoBackend(app.getApplicationContext());
GoBackend.setAlwaysOnCallback(() -> {
get().tunnelManager.restoreState(true).whenComplete(ExceptionLoggers.D);
});
}
app.backend = backend;
}
return app.backend;
}
}

public static CompletableFuture<Backend> getBackendAsync() {
return get().futureBackend;
}

public static ModuleLoader getModuleLoader() {
return get().moduleLoader;
}

public static RootShell getRootShell() {
return get().rootShell;
}

public static SharedPreferences getSharedPreferences() {
return get().sharedPreferences;
}

public static ToolsInstaller getToolsInstaller() {
return get().toolsInstaller;
}

public static TunnelManager getTunnelManager() {
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);
}

if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
}

@Override
public void onCreate() {
Log.i(TAG, USER_AGENT);
super.onCreate();

asyncWorker = new AsyncWorker(AsyncTask.SERIAL_EXECUTOR, new Handler(Looper.getMainLooper()));
rootShell = new RootShell(getApplicationContext());
toolsInstaller = new ToolsInstaller(getApplicationContext(), rootShell);
moduleLoader = new ModuleLoader(getApplicationContext(), rootShell, USER_AGENT);

sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
AppCompatDelegate.setDefaultNightMode(
sharedPreferences.getBoolean("dark_theme", false) ?
AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}

tunnelManager = new TunnelManager(new FileConfigStore(getApplicationContext()));
tunnelManager.onCreate();

asyncWorker.supplyAsync(Application::getBackend).thenAccept(futureBackend::complete);

sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}

@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
if ("multiple_tunnels".equals(key) && backend != null && backend instanceof WgQuickBackend)
((WgQuickBackend) backend).setMultipleTunnels(sharedPreferences.getBoolean(key, false));
}

@Override
public void onTerminate() {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
super.onTerminate();
}
}

+ 159
- 0
ui/src/main/java/com/wireguard/android/Application.kt 查看文件

@@ -0,0 +1,159 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android

import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.AsyncTask
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.StrictMode
import android.os.StrictMode.VmPolicy
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.configStore.FileConfigStore
import com.wireguard.android.model.TunnelManager
import com.wireguard.android.util.AsyncWorker
import com.wireguard.android.util.ExceptionLoggers
import com.wireguard.android.util.ModuleLoader
import com.wireguard.android.util.RootShell
import com.wireguard.android.util.ToolsInstaller
import java9.util.concurrent.CompletableFuture
import java.lang.ref.WeakReference
import java.util.Locale

class Application : android.app.Application(), OnSharedPreferenceChangeListener {
private val futureBackend = CompletableFuture<Backend>()
private lateinit var asyncWorker: AsyncWorker
private var backend: Backend? = null
private lateinit var moduleLoader: ModuleLoader
private lateinit var rootShell: RootShell
private lateinit var sharedPreferences: SharedPreferences
private lateinit var toolsInstaller: ToolsInstaller
private lateinit var tunnelManager: TunnelManager

override fun attachBaseContext(context: Context) {
super.attachBaseContext(context)
if (BuildConfig.MIN_SDK_VERSION > Build.VERSION.SDK_INT) {
val intent = 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)
}
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
}
}

override fun onCreate() {
Log.i(TAG, USER_AGENT)
super.onCreate()
asyncWorker = AsyncWorker(AsyncTask.SERIAL_EXECUTOR, Handler(Looper.getMainLooper()))
rootShell = RootShell(applicationContext)
toolsInstaller = ToolsInstaller(applicationContext, rootShell)
moduleLoader = ModuleLoader(applicationContext, rootShell, USER_AGENT)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
AppCompatDelegate.setDefaultNightMode(
if (sharedPreferences.getBoolean("dark_theme", false)) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
tunnelManager = TunnelManager(FileConfigStore(applicationContext))
tunnelManager.onCreate()
asyncWorker.supplyAsync { getBackend() }.thenAccept { futureBackend.complete(it) }
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if ("multiple_tunnels" == key && backend != null && backend is WgQuickBackend)
(backend as WgQuickBackend).setMultipleTunnels(sharedPreferences.getBoolean(key, false))
}

override fun onTerminate() {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
super.onTerminate()
}

companion object {
val USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, if (Build.SUPPORTED_ABIS.isNotEmpty()) Build.SUPPORTED_ABIS[0] else "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT)
private val TAG = "WireGuard/" + Application::class.java.simpleName
private lateinit var weakSelf: WeakReference<Application>

@JvmStatic
fun get(): Application {
return weakSelf.get()!!
}

@JvmStatic
fun getAsyncWorker() = get().asyncWorker

@JvmStatic
fun getBackend(): Backend {
val app = get()
synchronized(app.futureBackend) {
if (app.backend == null) {
var backend: Backend? = null
var didStartRootShell = false
if (!ModuleLoader.isModuleLoaded() && app.moduleLoader.moduleMightExist()) {
try {
app.rootShell.start()
didStartRootShell = true
app.moduleLoader.loadModule()
} catch (ignored: Exception) {
}
}
if (!app.sharedPreferences.getBoolean("disable_kernel_module", false) && ModuleLoader.isModuleLoaded()) {
try {
if (!didStartRootShell)
app.rootShell.start()
val wgQuickBackend = WgQuickBackend(app.applicationContext, app.rootShell, app.toolsInstaller)
wgQuickBackend.setMultipleTunnels(app.sharedPreferences.getBoolean("multiple_tunnels", false))
backend = wgQuickBackend
} catch (ignored: Exception) {
}
}
if (backend == null) {
backend = GoBackend(app.applicationContext)
GoBackend.setAlwaysOnCallback { get().tunnelManager.restoreState(true).whenComplete(ExceptionLoggers.D) }
}
app.backend = backend
}
return app.backend!!
}
}

@JvmStatic
fun getBackendAsync() = get().futureBackend

@JvmStatic
fun getModuleLoader() = get().moduleLoader

@JvmStatic
fun getRootShell() = get().rootShell

@JvmStatic
fun getSharedPreferences() = get().sharedPreferences

@JvmStatic
fun getToolsInstaller() = get().toolsInstaller

@JvmStatic
fun getTunnelManager() = get().tunnelManager
}

init {
weakSelf = WeakReference(this)
}
}

+ 0
- 40
ui/src/main/java/com/wireguard/android/BootShutdownReceiver.java 查看文件

@@ -1,40 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.wireguard.android.backend.WgQuickBackend;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.util.NonNullForAll;

@NonNullForAll
public class BootShutdownReceiver extends BroadcastReceiver {
private static final String TAG = "WireGuard/" + BootShutdownReceiver.class.getSimpleName();

@Override
public void onReceive(final Context context, final Intent intent) {
Application.getBackendAsync().thenAccept(backend -> {
if (!(backend instanceof WgQuickBackend))
return;
final String action = intent.getAction();
if (action == null)
return;
final TunnelManager tunnelManager = Application.getTunnelManager();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.i(TAG, "Broadcast receiver restoring state (boot)");
tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D);
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
tunnelManager.saveState();
}
});
}
}

+ 34
- 0
ui/src/main/java/com/wireguard/android/BootShutdownReceiver.kt 查看文件

@@ -0,0 +1,34 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.ExceptionLoggers

class BootShutdownReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Application.getBackendAsync().thenAccept { backend: Backend? ->
if (backend !is WgQuickBackend) return@thenAccept
val action = intent.action ?: return@thenAccept
val tunnelManager = Application.getTunnelManager()
if (Intent.ACTION_BOOT_COMPLETED == action) {
Log.i(TAG, "Broadcast receiver restoring state (boot)")
tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D)
} else if (Intent.ACTION_SHUTDOWN == action) {
Log.i(TAG, "Broadcast receiver saving state (shutdown)")
tunnelManager.saveState()
}
}
}

companion object {
private val TAG = "WireGuard/" + BootShutdownReceiver::class.java.simpleName
}
}

+ 0
- 177
ui/src/main/java/com/wireguard/android/QuickTileService.java 查看文件

@@ -1,177 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.IBinder;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.Log;

import com.wireguard.android.activity.MainActivity;
import com.wireguard.android.activity.TunnelToggleActivity;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.model.ObservableTunnel;
import com.wireguard.android.widget.SlashDrawable;
import com.wireguard.util.NonNullForAll;

import java.util.Objects;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.databinding.Observable;
import androidx.databinding.Observable.OnPropertyChangedCallback;

/**
* Service that maintains the application's custom Quick Settings tile. This service is bound by the
* system framework as necessary to update the appearance of the tile in the system UI, and to
* forward click events to the application.
*/

@RequiresApi(Build.VERSION_CODES.N)
@NonNullForAll
public class QuickTileService extends TileService {
private static final String TAG = "WireGuard/" + QuickTileService.class.getSimpleName();

private final OnStateChangedCallback onStateChangedCallback = new OnStateChangedCallback();
private final OnTunnelChangedCallback onTunnelChangedCallback = new OnTunnelChangedCallback();
@Nullable private Icon iconOff;
@Nullable private Icon iconOn;
@Nullable private ObservableTunnel tunnel;

/* This works around an annoying unsolved frameworks bug some people are hitting. */
@Override
@Nullable
public IBinder onBind(final Intent intent) {
IBinder ret = null;
try {
ret = super.onBind(intent);
} catch (final Exception e) {
Log.d(TAG, "Failed to bind to TileService", e);
}
return ret;
}

@Override
public void onClick() {
if (tunnel != null) {
unlockAndRun(() -> {
final Tile tile = getQsTile();
if (tile != null) {
tile.setIcon(tile.getIcon() == iconOn ? iconOff : iconOn);
tile.updateTile();
}
tunnel.setState(State.TOGGLE).whenComplete((v, t) -> {
if (t == null) {
updateTile();
} else {
final Intent toggleIntent = new Intent(this, TunnelToggleActivity.class);
toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(toggleIntent);
}
});
});
} else {
final Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityAndCollapse(intent);
}
}

@Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
iconOff = iconOn = Icon.createWithResource(this, R.drawable.ic_tile);
return;
}
final SlashDrawable icon = new SlashDrawable(getResources().getDrawable(R.drawable.ic_tile, Application.get().getTheme()));
icon.setAnimationEnabled(false); /* Unfortunately we can't have animations, since Icons are marshaled. */
icon.setSlashed(false);
Bitmap b = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
icon.setBounds(0, 0, c.getWidth(), c.getHeight());
icon.draw(c);
iconOn = Icon.createWithBitmap(b);
icon.setSlashed(true);
b = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
c = new Canvas(b);
icon.setBounds(0, 0, c.getWidth(), c.getHeight());
icon.draw(c);
iconOff = Icon.createWithBitmap(b);
}

@Override
public void onStartListening() {
Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback);
if (tunnel != null)
tunnel.addOnPropertyChangedCallback(onStateChangedCallback);
updateTile();
}

@Override
public void onStopListening() {
if (tunnel != null)
tunnel.removeOnPropertyChangedCallback(onStateChangedCallback);
Application.getTunnelManager().removeOnPropertyChangedCallback(onTunnelChangedCallback);
}

private void updateTile() {
// Update the tunnel.
final ObservableTunnel newTunnel = Application.getTunnelManager().getLastUsedTunnel();
if (newTunnel != tunnel) {
if (tunnel != null)
tunnel.removeOnPropertyChangedCallback(onStateChangedCallback);
tunnel = newTunnel;
if (tunnel != null)
tunnel.addOnPropertyChangedCallback(onStateChangedCallback);
}
// Update the tile contents.
final String label;
final int state;
final Tile tile = getQsTile();
if (tunnel != null) {
label = tunnel.getName();
state = tunnel.getState() == State.UP ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
} else {
label = getString(R.string.app_name);
state = Tile.STATE_INACTIVE;
}
if (tile == null)
return;
tile.setLabel(label);
if (tile.getState() != state) {
tile.setIcon(state == Tile.STATE_ACTIVE ? iconOn : iconOff);
tile.setState(state);
}
tile.updateTile();
}

private final class OnStateChangedCallback extends OnPropertyChangedCallback {
@Override
public void onPropertyChanged(final Observable sender, final int propertyId) {
if (!Objects.equals(sender, tunnel)) {
sender.removeOnPropertyChangedCallback(this);
return;
}
if (propertyId != 0 && propertyId != BR.state)
return;
updateTile();
}
}

private final class OnTunnelChangedCallback extends OnPropertyChangedCallback {
@Override
public void onPropertyChanged(final Observable sender, final int propertyId) {
if (propertyId != 0 && propertyId != BR.lastUsedTunnel)
return;
updateTile();
}
}
}

+ 158
- 0
ui/src/main/java/com/wireguard/android/QuickTileService.kt 查看文件

@@ -0,0 +1,158 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android

import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Icon
import android.os.Build
import android.os.IBinder
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.databinding.Observable
import androidx.databinding.Observable.OnPropertyChangedCallback
import com.wireguard.android.activity.MainActivity
import com.wireguard.android.activity.TunnelToggleActivity
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.widget.SlashDrawable

/**
* Service that maintains the application's custom Quick Settings tile. This service is bound by the
* system framework as necessary to update the appearance of the tile in the system UI, and to
* forward click events to the application.
*/
@RequiresApi(Build.VERSION_CODES.N)
class QuickTileService : TileService() {
private val onStateChangedCallback = OnStateChangedCallback()
private val onTunnelChangedCallback = OnTunnelChangedCallback()
private var iconOff: Icon? = null
private var iconOn: Icon? = null
private var tunnel: ObservableTunnel? = null

/* This works around an annoying unsolved frameworks bug some people are hitting. */
override fun onBind(intent: Intent): IBinder? {
var ret: IBinder? = null
try {
ret = super.onBind(intent)
} catch (e: Exception) {
Log.d(TAG, "Failed to bind to TileService", e)
}
return ret
}

override fun onClick() {
if (tunnel != null) {
unlockAndRun {
val tile = qsTile
if (tile != null) {
tile.icon = if (tile.icon == iconOn) iconOff else iconOn
tile.updateTile()
}
tunnel!!.setState(Tunnel.State.TOGGLE).whenComplete { _, t ->
if (t == null) {
updateTile()
} else {
val toggleIntent = Intent(this, TunnelToggleActivity::class.java)
toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(toggleIntent)
}
}
}
} else {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(intent)
}
}

override fun onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
iconOn = Icon.createWithResource(this, R.drawable.ic_tile)
iconOff = iconOn
return
}
val icon = SlashDrawable(resources.getDrawable(R.drawable.ic_tile, Application.get().theme))
icon.setAnimationEnabled(false) /* Unfortunately we can't have animations, since Icons are marshaled. */
icon.setSlashed(false)
var b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
?: return
var c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
iconOn = Icon.createWithBitmap(b)
icon.setSlashed(true)
b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
?: return
c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
iconOff = Icon.createWithBitmap(b)
}

override fun onStartListening() {
Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback)
if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
updateTile()
}

override fun onStopListening() {
if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
Application.getTunnelManager().removeOnPropertyChangedCallback(onTunnelChangedCallback)
}

private fun updateTile() {
// Update the tunnel.
val newTunnel = Application.getTunnelManager().lastUsedTunnel
if (newTunnel != tunnel) {
if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
tunnel = newTunnel
if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
}
// Update the tile contents.
val label: String
val state: Int
val tile = qsTile
if (tunnel != null) {
label = tunnel!!.name
state = if (tunnel!!.state == Tunnel.State.UP) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
} else {
label = getString(R.string.app_name)
state = Tile.STATE_INACTIVE
}
if (tile == null) return
tile.label = label
if (tile.state != state) {
tile.icon = if (state == Tile.STATE_ACTIVE) iconOn else iconOff
tile.state = state
}
tile.updateTile()
}

private inner class OnStateChangedCallback : OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
if (sender != tunnel) {
sender.removeOnPropertyChangedCallback(this)
return
}
if (propertyId != 0 && propertyId != BR.state) return
updateTile()
}
}

private inner class OnTunnelChangedCallback : OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
if (propertyId != 0 && propertyId != BR.lastUsedTunnel) return
updateTile()
}
}

companion object {
private val TAG = "WireGuard/" + QuickTileService::class.java.simpleName
}
}

+ 1
- 1
ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt 查看文件

@@ -43,7 +43,7 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
}

init {
Application.getBackendAsync().thenAccept { backend: Backend ->
Application.getBackendAsync().thenAccept { backend ->
versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH))
Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete { version, exception ->
versionSummary = if (exception == null)


Loading…
取消
儲存