diff --git a/android/src/main/java/com/tailscale/ipn/App.java b/android/src/main/java/com/tailscale/ipn/App.java index 103362a..9eb0fe9 100644 --- a/android/src/main/java/com/tailscale/ipn/App.java +++ b/android/src/main/java/com/tailscale/ipn/App.java @@ -98,11 +98,18 @@ public class App extends Application { ); } - void setTileStatus(boolean wantRunning) { + void setTileReady(boolean ready) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return; } - QuickToggleService.setStatus(this, wantRunning); + QuickToggleService.setReady(this, ready); + } + + void setTileStatus(boolean status) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + QuickToggleService.setStatus(this, status); } String getHostname() { diff --git a/android/src/main/java/com/tailscale/ipn/QuickToggleService.java b/android/src/main/java/com/tailscale/ipn/QuickToggleService.java index 64bbf0b..05405cd 100644 --- a/android/src/main/java/com/tailscale/ipn/QuickToggleService.java +++ b/android/src/main/java/com/tailscale/ipn/QuickToggleService.java @@ -6,6 +6,7 @@ package com.tailscale.ipn; import android.content.Context; import android.content.ComponentName; +import android.content.Intent; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; @@ -13,33 +14,68 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicBoolean; public class QuickToggleService extends TileService { - private static AtomicBoolean active = new AtomicBoolean(); - private static AtomicReference currentTile = new AtomicReference(); + // lock protects the static fields below it. + private static Object lock = new Object(); + // Active tracks whether the VPN is active. + private static boolean active; + // Ready tracks whether the tailscale backend is + // ready to switch on/off. + private static boolean ready; + // currentTile tracks getQsTile while service is listening. + private static Tile currentTile; @Override public void onStartListening() { - currentTile.set(getQsTile()); + synchronized (lock) { + currentTile = getQsTile(); + } updateTile(); } @Override public void onStopListening() { - currentTile.set(null); + synchronized (lock) { + currentTile = null; + } } @Override public void onClick() { - onTileClick(); + boolean r; + synchronized (lock) { + r = ready; + } + if (r) { + onTileClick(); + } else { + // Start main activity. + Intent i = getPackageManager().getLaunchIntentForPackage(getPackageName()); + startActivityAndCollapse(i); + } } private static void updateTile() { - Tile t = currentTile.get(); + Tile t; + boolean act; + synchronized (lock) { + t = currentTile; + act = active && ready; + } if (t == null) { return; } - t.setState(active.get() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); + t.setState(act ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); t.updateTile(); } - static void setStatus(Context ctx, boolean wantRunning) { - active.set(wantRunning); + static void setReady(Context ctx, boolean rdy) { + synchronized (lock) { + ready = rdy; + } + updateTile(); + } + + static void setStatus(Context ctx, boolean act) { + synchronized (lock) { + active = act; + } updateTile(); } diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 6705d20..934b250 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -468,6 +468,13 @@ func (a *App) notify(state BackendState) { case a.updates <- struct{}{}: default: } + ready := jni.False + if state.State >= ipn.Stopped { + ready = jni.True + } + if err := a.callVoidMethod(a.appCtx, "setTileReady", "(Z)V", jni.Value(ready)); err != nil { + fatalErr(err) + } } func (a *App) setPrefs(prefs *ipn.Prefs) {