summaryrefslogtreecommitdiffhomepage
path: root/app/src/main/java
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-06-01 07:34:00 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2018-06-01 14:57:25 +0200
commit32d669a661edec69f88f5d2646071de761b87630 (patch)
treec671e1583f9962373233e2c84c1409a91ea687dd /app/src/main/java
parent918076a6703268307e7856dcd5f25c4f3dc870ab (diff)
theme: add dark theme with toggle
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/com/wireguard/android/Application.java14
-rw-r--r--app/src/main/java/com/wireguard/android/activity/BaseActivity.java21
-rw-r--r--app/src/main/java/com/wireguard/android/activity/SettingsActivity.java45
-rw-r--r--app/src/main/java/com/wireguard/android/util/Topic.java97
4 files changed, 174 insertions, 3 deletions
diff --git a/app/src/main/java/com/wireguard/android/Application.java b/app/src/main/java/com/wireguard/android/Application.java
index 0066d930..b8562606 100644
--- a/app/src/main/java/com/wireguard/android/Application.java
+++ b/app/src/main/java/com/wireguard/android/Application.java
@@ -12,6 +12,7 @@ import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
+import android.support.v7.app.AppCompatDelegate;
import com.wireguard.android.backend.Backend;
import com.wireguard.android.backend.GoBackend;
@@ -22,6 +23,7 @@ import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.AsyncWorker;
import com.wireguard.android.util.RootShell;
import com.wireguard.android.util.ToolsInstaller;
+import com.wireguard.android.util.Topic;
import java.io.File;
import java.util.concurrent.Executor;
@@ -66,6 +68,8 @@ public class Application extends android.app.Application {
ToolsInstaller getToolsInstaller();
TunnelManager getTunnelManager();
+
+ Topic getThemeChangeTopic();
}
@Qualifier
@@ -86,6 +90,10 @@ public class Application extends android.app.Application {
private ApplicationModule(final Application application) {
context = application.getApplicationContext();
+
+ AppCompatDelegate.setDefaultNightMode(
+ getPreferences(context).getBoolean("dark_theme", false) ?
+ AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
}
@ApplicationScope
@@ -111,6 +119,12 @@ public class Application extends android.app.Application {
return new FileConfigStore(context);
}
+ @ApplicationScope
+ @Provides
+ public static Topic getThemeChangeTopic() {
+ return new Topic();
+ }
+
@ApplicationScope
@Provides
diff --git a/app/src/main/java/com/wireguard/android/activity/BaseActivity.java b/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
index a0f3c32f..16e66817 100644
--- a/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
@@ -16,6 +16,7 @@ import com.wireguard.android.Application;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.TunnelManager;
+import com.wireguard.android.util.Topic;
import java.util.Objects;
@@ -23,7 +24,7 @@ import java.util.Objects;
* Base class for activities that need to remember the currently-selected tunnel.
*/
-public abstract class BaseActivity extends AppCompatActivity {
+public abstract class BaseActivity extends AppCompatActivity implements Topic.Subscriber {
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
@@ -40,6 +41,8 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
+ subscribeTopics();
+
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
String savedTunnelName = null;
if (savedInstanceState != null)
@@ -63,6 +66,12 @@ public abstract class BaseActivity extends AppCompatActivity {
}
@Override
+ protected void onDestroy() {
+ unsubscribeTopics();
+ super.onDestroy();
+ }
+
+ @Override
protected void onSaveInstanceState(final Bundle outState) {
if (selectedTunnel != null)
outState.putString(KEY_SELECTED_TUNNEL, selectedTunnel.getName());
@@ -105,4 +114,14 @@ public abstract class BaseActivity extends AppCompatActivity {
super(new SelectionChangeNotifier());
}
}
+
+ @Override
+ public void onTopicPublished(Topic topic) {
+ recreate();
+ }
+
+ @Override
+ public Topic[] getSubscription() {
+ return new Topic[] { Application.getComponent().getThemeChangeTopic() };
+ }
}
diff --git a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
index 040c5efc..d358c532 100644
--- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
@@ -6,12 +6,14 @@
package com.wireguard.android.activity;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.app.AppCompatDelegate;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.view.MenuItem;
@@ -19,6 +21,7 @@ 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.Topic;
import java.util.ArrayList;
import java.util.Arrays;
@@ -30,7 +33,7 @@ import java.util.Map;
* Interface for changing application-global persistent settings.
*/
-public class SettingsActivity extends AppCompatActivity {
+public class SettingsActivity extends AppCompatActivity implements Topic.Subscriber {
private final Map<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>();
private int permissionRequestCounter;
@@ -56,6 +59,7 @@ public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ subscribeTopics();
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new SettingsFragment())
@@ -64,6 +68,12 @@ public class SettingsActivity extends AppCompatActivity {
}
@Override
+ protected void onDestroy() {
+ unsubscribeTopics();
+ super.onDestroy();
+ }
+
+ @Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@@ -89,7 +99,17 @@ public class SettingsActivity extends AppCompatActivity {
void done(String[] permissions, int[] grantResults);
}
- public static class SettingsFragment extends PreferenceFragmentCompat {
+ @Override
+ public void onTopicPublished(Topic topic) {
+ recreate();
+ }
+
+ @Override
+ public Topic[] getSubscription() {
+ return new Topic[] { Application.getComponent().getThemeChangeTopic() };
+ }
+
+ public static class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
addPreferencesFromResource(R.xml.preferences);
@@ -100,5 +120,26 @@ public class SettingsActivity extends AppCompatActivity {
getPreferenceScreen().removePreference(pref);
}
}
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if ("dark_theme".equals(key)) {
+ AppCompatDelegate.setDefaultNightMode(
+ sharedPreferences.getBoolean(key, false) ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
+ Application.getComponent().getThemeChangeTopic().publish(false);
+ }
+ }
}
}
diff --git a/app/src/main/java/com/wireguard/android/util/Topic.java b/app/src/main/java/com/wireguard/android/util/Topic.java
new file mode 100644
index 00000000..eed13c9e
--- /dev/null
+++ b/app/src/main/java/com/wireguard/android/util/Topic.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2017 John Wu <topjohnwu@gmail.com>
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+package com.wireguard.android.util;
+
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Topic {
+
+ private static final int NON_INIT = 0;
+ private static final int PENDING = 1;
+ private static final int PUBLISHED = 2;
+
+ private int state = NON_INIT;
+ private List<WeakReference<Subscriber>> subscribers;
+ private Object[] results;
+
+ public void subscribe(Subscriber sub) {
+ if (subscribers == null) {
+ subscribers = new LinkedList<>();
+ }
+ subscribers.add(new WeakReference<>(sub));
+ }
+
+ public void unsubscribe() {
+ subscribers = null;
+ }
+
+ public void unsubscribe(Subscriber sub) {
+ for (Iterator<WeakReference<Subscriber>> i = subscribers.iterator(); i.hasNext();) {
+ WeakReference<Subscriber> subscriber = i.next();
+ if (subscriber.get() == null || subscriber.get() == sub) {
+ i.remove();
+ }
+ }
+ }
+
+ public void reset() {
+ state = NON_INIT;
+ results = null;
+ }
+
+ public boolean isPublished() {
+ return state == PUBLISHED;
+ }
+
+ public void publish() {
+ publish(true);
+ }
+
+ public void publish(boolean record, Object... results) {
+ if (record)
+ state = PUBLISHED;
+ this.results = results;
+ if (subscribers != null) {
+ for (WeakReference<Subscriber> subscriber : subscribers) {
+ if (subscriber.get() != null)
+ subscriber.get().onTopicPublished(this);
+ }
+ }
+ }
+
+ public Object[] getResults() {
+ return results;
+ }
+
+ public boolean isPending() {
+ return state == PENDING;
+ }
+
+ public void setPending() {
+ state = PENDING;
+ }
+
+ public interface Subscriber {
+ default void subscribeTopics() {
+ for (Topic topic : getSubscription()) {
+ if (topic.isPublished()) {
+ onTopicPublished(topic);
+ }
+ topic.subscribe(this);
+ }
+ }
+ default void unsubscribeTopics() {
+ for (Topic event : getSubscription()) {
+ event.unsubscribe(this);
+ }
+ }
+ void onTopicPublished(Topic topic);
+ Topic[] getSubscription();
+ }
+}