2017-08-17 12:45:40 +02:00
|
|
|
package com.wireguard.android;
|
|
|
|
|
|
|
|
import android.annotation.TargetApi;
|
|
|
|
import android.content.Intent;
|
2018-01-01 09:06:37 +01:00
|
|
|
import android.databinding.Observable;
|
|
|
|
import android.databinding.Observable.OnPropertyChangedCallback;
|
2017-08-17 12:45:40 +02:00
|
|
|
import android.graphics.drawable.Icon;
|
|
|
|
import android.os.Build;
|
|
|
|
import android.service.quicksettings.Tile;
|
|
|
|
import android.service.quicksettings.TileService;
|
2018-01-01 09:06:37 +01:00
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.Toast;
|
2017-08-17 12:45:40 +02:00
|
|
|
|
2018-01-01 09:06:37 +01:00
|
|
|
import com.wireguard.android.activity.MainActivity;
|
|
|
|
import com.wireguard.android.model.Tunnel;
|
|
|
|
import com.wireguard.android.model.Tunnel.State;
|
|
|
|
import com.wireguard.android.model.TunnelManager;
|
2018-01-08 20:30:25 +01:00
|
|
|
import com.wireguard.android.util.ExceptionLoggers;
|
|
|
|
import com.wireguard.android.util.RootShell;
|
2018-01-01 09:06:37 +01:00
|
|
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2017-08-17 12:45:40 +02:00
|
|
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.N)
|
2018-01-08 04:50:43 +01:00
|
|
|
public class QuickTileService extends TileService {
|
2018-01-08 20:29:51 +01:00
|
|
|
private static final String TAG = "WireGuard/" + QuickTileService.class.getSimpleName();
|
2018-01-08 04:50:43 +01:00
|
|
|
private final OnStateChangedCallback onStateChangedCallback = new OnStateChangedCallback();
|
|
|
|
private final OnTunnelChangedCallback onTunnelChangedCallback = new OnTunnelChangedCallback();
|
2018-01-01 09:06:37 +01:00
|
|
|
private Tunnel tunnel;
|
|
|
|
private TunnelManager tunnelManager;
|
2017-08-17 12:45:40 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClick() {
|
2018-01-08 04:50:43 +01:00
|
|
|
if (tunnel != null)
|
|
|
|
tunnel.setState(State.TOGGLE).whenComplete(this::onToggleFinished);
|
|
|
|
else
|
|
|
|
startActivityAndCollapse(new Intent(this, MainActivity.class));
|
2017-08-17 12:45:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
2018-01-01 09:06:37 +01:00
|
|
|
super.onCreate();
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnelManager = Application.getComponent().getTunnelManager();
|
2017-08-17 12:45:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onStartListening() {
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnelManager.addOnPropertyChangedCallback(onTunnelChangedCallback);
|
2018-01-01 09:06:37 +01:00
|
|
|
if (tunnel != null)
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnel.addOnPropertyChangedCallback(onStateChangedCallback);
|
2018-01-01 09:06:37 +01:00
|
|
|
updateTile();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onStopListening() {
|
|
|
|
if (tunnel != null)
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnel.removeOnPropertyChangedCallback(onStateChangedCallback);
|
|
|
|
tunnelManager.removeOnPropertyChangedCallback(onTunnelChangedCallback);
|
2018-01-01 09:06:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
private Void onToggleFinished(final State state, final Throwable throwable) {
|
|
|
|
if (throwable == null)
|
|
|
|
return null;
|
|
|
|
Log.e(TAG, "Cannot toggle tunnel", throwable);
|
2018-01-08 20:30:25 +01:00
|
|
|
final String message = throwable instanceof RootShell.NoRootException ?
|
|
|
|
getApplicationContext().getString(R.string.error_rootshell) :
|
|
|
|
getApplicationContext().getString(R.string.error_toggle) + ": " + throwable.getMessage();
|
|
|
|
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
|
2018-01-01 09:06:37 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateTile() {
|
|
|
|
// Update the tunnel.
|
2018-01-08 04:50:43 +01:00
|
|
|
final Tunnel newTunnel = tunnelManager.getLastUsedTunnel();
|
|
|
|
if (newTunnel != tunnel) {
|
2018-01-01 09:06:37 +01:00
|
|
|
if (tunnel != null)
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnel.removeOnPropertyChangedCallback(onStateChangedCallback);
|
2018-01-01 09:06:37 +01:00
|
|
|
tunnel = newTunnel;
|
|
|
|
if (tunnel != null)
|
2018-01-08 04:50:43 +01:00
|
|
|
tunnel.addOnPropertyChangedCallback(onStateChangedCallback);
|
2018-01-01 09:06:37 +01:00
|
|
|
}
|
|
|
|
// Update the tile contents.
|
|
|
|
final String label;
|
|
|
|
final int state;
|
2017-08-17 12:45:40 +02:00
|
|
|
final Tile tile = getQsTile();
|
2018-01-01 09:06:37 +01:00
|
|
|
if (tunnel != null) {
|
|
|
|
label = tunnel.getName();
|
|
|
|
state = tunnel.getState() == Tunnel.State.UP ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
|
2017-08-17 12:45:40 +02:00
|
|
|
} else {
|
2018-01-01 09:06:37 +01:00
|
|
|
label = getString(R.string.app_name);
|
|
|
|
state = Tile.STATE_INACTIVE;
|
|
|
|
}
|
|
|
|
tile.setLabel(label);
|
|
|
|
if (tile.getState() != state) {
|
|
|
|
// The icon must be changed every time the state changes, or the shade will not change.
|
2018-01-08 04:50:43 +01:00
|
|
|
final Integer iconResource = state == Tile.STATE_ACTIVE ? R.drawable.ic_tile
|
|
|
|
: R.drawable.ic_tile_disabled;
|
2018-01-01 09:06:37 +01:00
|
|
|
tile.setIcon(Icon.createWithResource(this, iconResource));
|
|
|
|
tile.setState(state);
|
2017-08-17 12:45:40 +02:00
|
|
|
}
|
|
|
|
tile.updateTile();
|
|
|
|
}
|
|
|
|
|
2018-01-08 04:50:43 +01:00
|
|
|
private final class OnStateChangedCallback extends OnPropertyChangedCallback {
|
2017-08-17 12:45:40 +02:00
|
|
|
@Override
|
2018-01-01 09:06:37 +01:00
|
|
|
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();
|
2017-08-17 12:45:40 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-08 04:50:43 +01:00
|
|
|
|
|
|
|
private final class OnTunnelChangedCallback extends OnPropertyChangedCallback {
|
|
|
|
@Override
|
|
|
|
public void onPropertyChanged(final Observable sender, final int propertyId) {
|
|
|
|
if (propertyId != 0 && propertyId != BR.lastUsedTunnel)
|
|
|
|
return;
|
|
|
|
updateTile();
|
|
|
|
}
|
|
|
|
}
|
2017-08-17 12:45:40 +02:00
|
|
|
}
|