diff --git a/app/src/main/java/com/wireguard/android/Application.java b/app/src/main/java/com/wireguard/android/Application.java index b8562606..74cb07cf 100644 --- a/app/src/main/java/com/wireguard/android/Application.java +++ b/app/src/main/java/com/wireguard/android/Application.java @@ -23,7 +23,6 @@ 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; @@ -68,8 +67,6 @@ public class Application extends android.app.Application { ToolsInstaller getToolsInstaller(); TunnelManager getTunnelManager(); - - Topic getThemeChangeTopic(); } @Qualifier @@ -119,13 +116,6 @@ public class Application extends android.app.Application { return new FileConfigStore(context); } - @ApplicationScope - @Provides - public static Topic getThemeChangeTopic() { - return new Topic(); - } - - @ApplicationScope @Provides public static Executor getExecutor() { 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 2ebb6e90..a44035f9 100644 --- a/app/src/main/java/com/wireguard/android/activity/BaseActivity.java +++ b/app/src/main/java/com/wireguard/android/activity/BaseActivity.java @@ -11,24 +11,19 @@ import android.databinding.CallbackRegistry; import android.databinding.CallbackRegistry.NotifierCallback; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; -import android.util.Log; 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.lang.reflect.Field; import java.util.Objects; /** * Base class for activities that need to remember the currently-selected tunnel. */ -public abstract class BaseActivity extends AppCompatActivity implements Topic.Subscriber { - private static final String TAG = "WireGuard/" + BaseActivity.class.getSimpleName(); - +public abstract class BaseActivity extends ThemeChangeAwareActivity { private static final String KEY_SELECTED_TUNNEL = "selected_tunnel"; private final SelectionChangeRegistry selectionChangeRegistry = new SelectionChangeRegistry(); @@ -45,8 +40,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su @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) @@ -69,12 +62,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su } } - @Override - protected void onDestroy() { - unsubscribeTopics(); - super.onDestroy(); - } - @Override protected void onSaveInstanceState(final Bundle outState) { if (selectedTunnel != null) @@ -118,35 +105,4 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Su 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() }; - } } 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 d7238b96..cfc9f3ef 100644 --- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java +++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java @@ -21,7 +21,6 @@ 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; @@ -33,7 +32,7 @@ import java.util.Map; * Interface for changing application-global persistent settings. */ -public class SettingsActivity extends AppCompatActivity implements Topic.Subscriber { +public class SettingsActivity extends ThemeChangeAwareActivity { private final Map permissionRequestCallbacks = new HashMap<>(); private int permissionRequestCounter; @@ -59,7 +58,6 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri @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()) @@ -67,12 +65,6 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri } } - @Override - protected void onDestroy() { - unsubscribeTopics(); - super.onDestroy(); - } - @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { @@ -99,18 +91,7 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri void done(String[] permissions, int[] grantResults); } - @Override - 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 { + public static class SettingsFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(final Bundle savedInstanceState, final String key) { addPreferencesFromResource(R.xml.preferences); @@ -121,26 +102,5 @@ public class SettingsActivity extends AppCompatActivity implements Topic.Subscri 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); - } - } } } diff --git a/app/src/main/java/com/wireguard/android/activity/ThemeChangeAwareActivity.java b/app/src/main/java/com/wireguard/android/activity/ThemeChangeAwareActivity.java new file mode 100644 index 00000000..250cf0a7 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/activity/ThemeChangeAwareActivity.java @@ -0,0 +1,76 @@ +/* + * Copyright © 2018 Jason A. Donenfeld . 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(); + } + } +} diff --git a/app/src/main/java/com/wireguard/android/util/Topic.java b/app/src/main/java/com/wireguard/android/util/Topic.java deleted file mode 100644 index 6d8080cf..00000000 --- a/app/src/main/java/com/wireguard/android/util/Topic.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright © 2017 John Wu - * 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> 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> subs = subscribers; - subscribers = new ArrayList<>(); - for (final WeakReference 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> subs = subscribers; - for (final WeakReference 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 extends ArrayList { - @Override - public synchronized boolean add(final E e) { - return super.add(e); - } - } -}