theme: add dark theme with toggle
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
918076a670
commit
32d669a661
@ -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
|
||||
|
@ -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)
|
||||
@ -62,6 +65,12 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unsubscribeTopics();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(final Bundle outState) {
|
||||
if (selectedTunnel != null)
|
||||
@ -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() };
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
@ -63,6 +67,12 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unsubscribeTopics();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
97
app/src/main/java/com/wireguard/android/util/Topic.java
Normal file
97
app/src/main/java/com/wireguard/android/util/Topic.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -29,6 +29,9 @@
|
||||
<string name="create_activity_title">Create WireGuard Tunnel</string>
|
||||
<string name="create_empty">Create from scratch</string>
|
||||
<string name="create_from_file">Create from file or archive</string>
|
||||
<string name="dark_theme_title">Use dark theme</string>
|
||||
<string name="dark_theme_summary_on">Currently using dark night theme</string>
|
||||
<string name="dark_theme_summary_off">Currently using light day theme</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="dns_servers">DNS servers</string>
|
||||
<string name="edit">Edit</string>
|
||||
|
@ -7,4 +7,10 @@
|
||||
android:title="@string/restore_on_boot_title" />
|
||||
<com.wireguard.android.preference.ToolsInstallerPreference android:key="tools_installer" />
|
||||
<com.wireguard.android.preference.ZipExporterPreference android:key="zip_exporter" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="dark_theme"
|
||||
android:summaryOn="@string/dark_theme_summary_on"
|
||||
android:summaryOff="@string/dark_theme_summary_off"
|
||||
android:title="@string/dark_theme_title" />
|
||||
</PreferenceScreen>
|
||||
|
Loading…
Reference in New Issue
Block a user