DarkMode: move to shared preferences listener
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
d56e95c576
commit
27072972ab
@ -23,7 +23,6 @@ import com.wireguard.android.model.TunnelManager;
|
|||||||
import com.wireguard.android.util.AsyncWorker;
|
import com.wireguard.android.util.AsyncWorker;
|
||||||
import com.wireguard.android.util.RootShell;
|
import com.wireguard.android.util.RootShell;
|
||||||
import com.wireguard.android.util.ToolsInstaller;
|
import com.wireguard.android.util.ToolsInstaller;
|
||||||
import com.wireguard.android.util.Topic;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@ -68,8 +67,6 @@ public class Application extends android.app.Application {
|
|||||||
ToolsInstaller getToolsInstaller();
|
ToolsInstaller getToolsInstaller();
|
||||||
|
|
||||||
TunnelManager getTunnelManager();
|
TunnelManager getTunnelManager();
|
||||||
|
|
||||||
Topic getThemeChangeTopic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@ -119,13 +116,6 @@ public class Application extends android.app.Application {
|
|||||||
return new FileConfigStore(context);
|
return new FileConfigStore(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApplicationScope
|
|
||||||
@Provides
|
|
||||||
public static Topic getThemeChangeTopic() {
|
|
||||||
return new Topic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ApplicationScope
|
@ApplicationScope
|
||||||
@Provides
|
@Provides
|
||||||
public static Executor getExecutor() {
|
public static Executor getExecutor() {
|
||||||
|
@ -11,24 +11,19 @@ import android.databinding.CallbackRegistry;
|
|||||||
import android.databinding.CallbackRegistry.NotifierCallback;
|
import android.databinding.CallbackRegistry.NotifierCallback;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.wireguard.android.Application;
|
import com.wireguard.android.Application;
|
||||||
import com.wireguard.android.backend.GoBackend;
|
import com.wireguard.android.backend.GoBackend;
|
||||||
import com.wireguard.android.model.Tunnel;
|
import com.wireguard.android.model.Tunnel;
|
||||||
import com.wireguard.android.model.TunnelManager;
|
import com.wireguard.android.model.TunnelManager;
|
||||||
import com.wireguard.android.util.Topic;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for activities that need to remember the currently-selected tunnel.
|
* Base class for activities that need to remember the currently-selected tunnel.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class BaseActivity extends AppCompatActivity implements Topic.Subscriber {
|
public abstract class BaseActivity extends ThemeChangeAwareActivity {
|
||||||
private static final String TAG = "WireGuard/" + BaseActivity.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
|
private static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
|
||||||
|
|
||||||
private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
|
private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry();
|
||||||
@ -45,8 +40,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
subscribeTopics();
|
|
||||||
|
|
||||||
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
|
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
|
||||||
String savedTunnelName = null;
|
String savedTunnelName = null;
|
||||||
if (savedInstanceState != null)
|
if (savedInstanceState != null)
|
||||||
@ -69,12 +62,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
unsubscribeTopics();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(final Bundle outState) {
|
protected void onSaveInstanceState(final Bundle outState) {
|
||||||
if (selectedTunnel != null)
|
if (selectedTunnel != null)
|
||||||
@ -118,35 +105,4 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su
|
|||||||
super(new SelectionChangeNotifier());
|
super(new SelectionChangeNotifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTopicPublished(final Topic topic) {
|
|
||||||
if (topic == Application.getComponent().getThemeChangeTopic()) {
|
|
||||||
try {
|
|
||||||
Field f;
|
|
||||||
Object o = getResources();
|
|
||||||
try {
|
|
||||||
f = o.getClass().getDeclaredField("mResourcesImpl");
|
|
||||||
f.setAccessible(true);
|
|
||||||
o = f.get(o);
|
|
||||||
} catch (final Exception ignored) { }
|
|
||||||
f = o.getClass().getDeclaredField("mDrawableCache");
|
|
||||||
f.setAccessible(true);
|
|
||||||
o = f.get(o);
|
|
||||||
try {
|
|
||||||
o.getClass().getMethod("onConfigurationChange", int.class).invoke(o, -1);
|
|
||||||
} catch (final Exception ignored) {
|
|
||||||
o.getClass().getMethod("clear").invoke(o);
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
Log.e(TAG, "Failed to flush drawable cache", e);
|
|
||||||
}
|
|
||||||
recreate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { Application.getComponent().getThemeChangeTopic() };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import android.view.MenuItem;
|
|||||||
import com.wireguard.android.Application;
|
import com.wireguard.android.Application;
|
||||||
import com.wireguard.android.R;
|
import com.wireguard.android.R;
|
||||||
import com.wireguard.android.backend.WgQuickBackend;
|
import com.wireguard.android.backend.WgQuickBackend;
|
||||||
import com.wireguard.android.util.Topic;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -33,7 +32,7 @@ import java.util.Map;
|
|||||||
* Interface for changing application-global persistent settings.
|
* Interface for changing application-global persistent settings.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SettingsActivity extends AppCompatActivity implements Topic.Subscriber {
|
public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||||
private final Map<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>();
|
private final Map<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>();
|
||||||
private int permissionRequestCounter;
|
private int permissionRequestCounter;
|
||||||
|
|
||||||
@ -59,7 +58,6 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
subscribeTopics();
|
|
||||||
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
|
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(android.R.id.content, new SettingsFragment())
|
.add(android.R.id.content, new SettingsFragment())
|
||||||
@ -67,12 +65,6 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
unsubscribeTopics();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
@ -99,18 +91,7 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri
|
|||||||
void done(String[] permissions, int[] grantResults);
|
void done(String[] permissions, int[] grantResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
public void onTopicPublished(final Topic topic) {
|
|
||||||
if (topic == Application.getComponent().getThemeChangeTopic())
|
|
||||||
recreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { Application.getComponent().getThemeChangeTopic() };
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
@ -121,26 +102,5 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri
|
|||||||
getPreferenceScreen().removePreference(pref);
|
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(final SharedPreferences sharedPreferences, final 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.wireguard.android.activity;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.app.AppCompatDelegate;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public abstract class ThemeChangeAwareActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
private static final String TAG = "WireGuard/" + ThemeChangeAwareActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static Resources lastResources;
|
||||||
|
private static boolean lastDarkMode;
|
||||||
|
private static synchronized void invalidateDrawableCache(final Resources resources, final boolean darkMode) {
|
||||||
|
if (resources == lastResources && darkMode == lastDarkMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field f;
|
||||||
|
Object o = resources;
|
||||||
|
try {
|
||||||
|
f = o.getClass().getDeclaredField("mResourcesImpl");
|
||||||
|
f.setAccessible(true);
|
||||||
|
o = f.get(o);
|
||||||
|
} catch (final Exception ignored) { }
|
||||||
|
f = o.getClass().getDeclaredField("mDrawableCache");
|
||||||
|
f.setAccessible(true);
|
||||||
|
o = f.get(o);
|
||||||
|
try {
|
||||||
|
o.getClass().getMethod("onConfigurationChange", int.class).invoke(o, -1);
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
o.getClass().getMethod("clear").invoke(o);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
Log.e(TAG, "Failed to flush drawable cache", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastResources = resources;
|
||||||
|
lastDarkMode = darkMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
|
||||||
|
if ("dark_theme".equals(key)) {
|
||||||
|
final boolean darkMode = sharedPreferences.getBoolean(key, false);
|
||||||
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
|
sharedPreferences.getBoolean(key, false) ?
|
||||||
|
AppCompatDelegate.MODE_NIGHT_YES :
|
||||||
|
AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
invalidateDrawableCache(getResources(), darkMode);
|
||||||
|
recreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
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 Topic() {
|
|
||||||
subscribers = new SyncArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void subscribe(final Subscriber sub) {
|
|
||||||
subscribers.add(new WeakReference<>(sub));
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void unsubscribe() {
|
|
||||||
subscribers = new SyncArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void unsubscribe(final Subscriber sub) {
|
|
||||||
List<WeakReference<Subscriber>> subs = subscribers;
|
|
||||||
subscribers = new ArrayList<>();
|
|
||||||
for (final WeakReference<Subscriber> subscriber : subs) {
|
|
||||||
if (subscriber.get() != null && subscriber.get() != sub)
|
|
||||||
subscribers.add(subscriber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
// Snapshot
|
|
||||||
List<WeakReference<Subscriber>> subs = subscribers;
|
|
||||||
for (final WeakReference<Subscriber> subscriber : subs) {
|
|
||||||
if (subscriber != null && 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 (final Topic topic : getSubscription()) {
|
|
||||||
if (topic.isPublished()) {
|
|
||||||
onTopicPublished(topic);
|
|
||||||
}
|
|
||||||
topic.subscribe(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default void unsubscribeTopics() {
|
|
||||||
for (final Topic event : getSubscription()) {
|
|
||||||
event.unsubscribe(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void onTopicPublished(Topic topic);
|
|
||||||
Topic[] getSubscription();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SyncArrayList<E> extends ArrayList<E> {
|
|
||||||
@Override
|
|
||||||
public synchronized boolean add(final E e) {
|
|
||||||
return super.add(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user