Rewrite Topics

This commit is contained in:
topjohnwu 2018-08-01 00:47:31 +08:00
parent 23c7bbc7d5
commit 863b9a410f
16 changed files with 150 additions and 155 deletions

View File

@ -42,7 +42,7 @@ import butterknife.OnClick;
import butterknife.Unbinder; import butterknife.Unbinder;
public class MagiskFragment extends Fragment public class MagiskFragment extends Fragment
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView { implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
private Container expandableContainer = new Container(); private Container expandableContainer = new Container();
@ -167,8 +167,7 @@ public class MagiskFragment extends Fragment
safetyNetStatusText.setText(R.string.safetyNet_check_text); safetyNetStatusText.setText(R.string.safetyNet_check_text);
mm.safetyNetDone.reset(); Topic.reset(getSubscribedTopics());
mm.updateCheckDone.reset();
Data.remoteMagiskVersionString = null; Data.remoteMagiskVersionString = null;
Data.remoteMagiskVersionCode = -1; Data.remoteMagiskVersionCode = -1;
collapse(); collapse();
@ -184,17 +183,20 @@ public class MagiskFragment extends Fragment
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
if (topic == mm.updateCheckDone) { return new int[] {Topic.SNET_CHECK_DONE, Topic.UPDATE_CHECK_DONE};
updateCheckUI();
} else if (topic == mm.safetyNetDone) {
updateSafetyNetUI((int) topic.getResults()[0]);
}
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { mm.updateCheckDone, mm.safetyNetDone }; switch (topic) {
case Topic.SNET_CHECK_DONE:
updateSafetyNetUI((int) result[0]);
break;
case Topic.UPDATE_CHECK_DONE:
updateCheckUI();
break;
}
} }
@Override @Override

View File

@ -80,13 +80,13 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
mSwipeRefreshLayout.setRefreshing(false); return new int[] {Topic.MAGISK_HIDE_DONE};
appAdapter.filter(null);
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { getApplication().magiskHideDone }; mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(null);
} }
} }

View File

@ -10,7 +10,6 @@ import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils; import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.ContainerApp; import com.topjohnwu.superuser.ContainerApp;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
@ -20,15 +19,6 @@ import java.util.Map;
public class MagiskManager extends ContainerApp { public class MagiskManager extends ContainerApp {
// Topics
public final Topic magiskHideDone = new Topic();
public final Topic reloadActivity = new Topic();
public final Topic moduleLoadDone = new Topic();
public final Topic repoLoadDone = new Topic();
public final Topic updateCheckDone = new Topic();
public final Topic safetyNetDone = new Topic();
public final Topic localeDone = new Topic();
// Info // Info
public boolean hasInit = false; public boolean hasInit = false;

View File

@ -111,13 +111,13 @@ public class MainActivity extends Activity
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
recreate(); return new int[] {Topic.RELOAD_ACTIVITY};
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { getMagiskManager().reloadActivity }; recreate();
} }
public void checkHideSection() { public void checkHideSection() {

View File

@ -4,6 +4,7 @@ import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@ -49,7 +50,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false); View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view); unbinder = ButterKnife.bind(this, view);
setHasOptionsMenu(true); setHasOptionsMenu(true);
@ -71,19 +72,19 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
} }
}); });
getActivity().setTitle(R.string.modules); requireActivity().setTitle(R.string.modules);
return view; return view;
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
updateUI(); return new int[] {Topic.MODULE_LOAD_DONE};
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { getApplication().moduleLoadDone }; updateUI();
} }
@Override @Override

View File

@ -46,7 +46,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
unbinder = ButterKnife.bind(this, view); unbinder = ButterKnife.bind(this, view);
mm = getApplication(); mm = getApplication();
mSwipeRefreshLayout.setRefreshing(mm.repoLoadDone.isPending()); mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> { mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.VISIBLE);
@ -73,15 +73,15 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
mSwipeRefreshLayout.setRefreshing(false); return new int[] {Topic.REPO_LOAD_DONE};
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { mm.repoLoadDone }; mSwipeRefreshLayout.setRefreshing(false);
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
} }
@Override @Override

View File

@ -73,17 +73,18 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public int[] getSubscribedTopics() {
recreate(); return new int[] {Topic.RELOAD_ACTIVITY};
} }
@Override @Override
public Topic[] getSubscription() { public void onPublish(int topic, Object[] result) {
return new Topic[] { getMagiskManager().reloadActivity }; recreate();
} }
public static class SettingsFragment extends PreferenceFragmentCompat public static class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber { implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber {
private SharedPreferences prefs; private SharedPreferences prefs;
private PreferenceScreen prefScreen; private PreferenceScreen prefScreen;
@ -229,14 +230,14 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
prefs.registerOnSharedPreferenceChangeListener(this); prefs.registerOnSharedPreferenceChangeListener(this);
subscribeTopics(); Topic.subscribe(this);
return super.onCreateView(inflater, container, savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState);
} }
@Override @Override
public void onDestroyView() { public void onDestroyView() {
prefs.unregisterOnSharedPreferenceChangeListener(this); prefs.unregisterOnSharedPreferenceChangeListener(this);
unsubscribeTopics(); Topic.unsubscribe(this);
super.onDestroyView(); super.onDestroyView();
} }
@ -246,7 +247,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
switch (key) { switch (key) {
case Const.Key.DARK_THEME: case Const.Key.DARK_THEME:
Data.isDarkTheme = prefs.getBoolean(key, false); Data.isDarkTheme = prefs.getBoolean(key, false);
mm.reloadActivity.publish(false); Topic.publish(false, Topic.RELOAD_ACTIVITY);
return; return;
case Const.Key.COREONLY: case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) { if (prefs.getBoolean(key, false)) {
@ -283,7 +284,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
break; break;
case Const.Key.LOCALE: case Const.Key.LOCALE:
LocaleManager.setLocale(); LocaleManager.setLocale();
mm.reloadActivity.publish(false); Topic.publish(false, Topic.RELOAD_ACTIVITY);
break; break;
case Const.Key.UPDATE_CHANNEL: case Const.Key.UPDATE_CHANNEL:
new CheckUpdates().exec(); new CheckUpdates().exec();
@ -314,13 +315,13 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public void onPublish(int topic, Object[] result) {
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE)); setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
} }
@Override @Override
public Topic[] getSubscription() { public int[] getSubscribedTopics() {
return new Topic[] { mm.localeDone }; return new int[] {Topic.LOCAL_FETCH_DONE};
} }
} }

View File

@ -16,6 +16,7 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.util.ArrayList; import java.util.ArrayList;
@ -152,7 +153,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
Data.MM().magiskHideDone.publish(false); Topic.publish(false, Topic.MAGISK_HIDE_DONE);
} }
} }
} }

View File

@ -5,6 +5,7 @@ import android.app.Activity;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper; import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils; import com.topjohnwu.superuser.ShellUtils;
@ -50,7 +51,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
Class.class, String.class, Activity.class, Object.class) Class.class, String.class, Activity.class, Object.class)
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(), .invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
(ISafetyNetHelper.Callback) code -> (ISafetyNetHelper.Callback) code ->
Data.MM().safetyNetDone.publish(false, code)); Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() != Const.SNET_VER) { if (helper.getVersion() != Const.SNET_VER) {
throw new Exception(); throw new Exception();
} }
@ -79,7 +80,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
helper.attest(); helper.attest();
} else { } else {
e.printStackTrace(); e.printStackTrace();
Data.MM().safetyNetDone.publish(false, -1); Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
} }
super.onPostExecute(e); super.onPostExecute(e);
} }

View File

@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data; import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.NotificationMgr; import com.topjohnwu.magisk.utils.NotificationMgr;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONException; import org.json.JSONException;
@ -93,7 +94,6 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
MagiskManager mm = Data.MM();
if (showNotification) { if (showNotification) {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) { if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
NotificationMgr.managerUpdate(); NotificationMgr.managerUpdate();
@ -101,7 +101,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
NotificationMgr.magiskUpdate(); NotificationMgr.magiskUpdate();
} }
} }
mm.updateCheckDone.publish(); Topic.publish(Topic.UPDATE_CHECK_DONE);
super.onPostExecute(v); super.onPostExecute(v);
} }
} }

View File

@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Module; import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap; import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFile;
public class LoadModules extends ParallelTask<Void, Void, Void> { public class LoadModules extends ParallelTask<Void, Void, Void> {
@ -29,7 +30,7 @@ public class LoadModules extends ParallelTask<Void, Void, Void> {
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
Data.MM().moduleLoadDone.publish(); Topic.publish(Topic.MODULE_LOAD_DONE);
super.onPostExecute(v); super.onPostExecute(v);
} }
} }

View File

@ -9,6 +9,7 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.ReposFragment; import com.topjohnwu.magisk.ReposFragment;
import com.topjohnwu.magisk.container.Repo; import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
@ -49,8 +50,8 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
private ExecutorService threadPool; private ExecutorService threadPool;
public UpdateRepos(boolean force) { public UpdateRepos(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
mm = Data.MM(); mm = Data.MM();
mm.repoLoadDone.reset();
forceUpdate = force; forceUpdate = force;
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE); threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
} }
@ -147,11 +148,6 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
ReposFragment.adapter.notifyDBChanged(); ReposFragment.adapter.notifyDBChanged();
} }
@Override
protected void onPreExecute() {
mm.repoLoadDone.setPending();
}
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(",")); etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
@ -186,7 +182,7 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
mm.repoLoadDone.publish(); Topic.publish(Topic.REPO_LOAD_DONE);
super.onPostExecute(v); super.onPostExecute(v);
} }
} }

View File

@ -14,9 +14,10 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager; import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
public abstract class FlavorActivity extends AppCompatActivity { public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
private ActivityResultListener activityResultListener; private ActivityResultListener activityResultListener;
static int[] EMPTY_INT_ARRAY = new int[0];
public FlavorActivity() { public FlavorActivity() {
super(); super();
@ -25,6 +26,11 @@ public abstract class FlavorActivity extends AppCompatActivity {
applyOverrideConfiguration(configuration); applyOverrideConfiguration(configuration);
} }
@Override
public int[] getSubscribedTopics() {
return EMPTY_INT_ARRAY;
}
@StyleRes @StyleRes
public int getDarkTheme() { public int getDarkTheme() {
return -1; return -1;
@ -37,9 +43,7 @@ public abstract class FlavorActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (this instanceof Topic.Subscriber) { Topic.subscribe(this);
((Topic.Subscriber) this).subscribeTopics();
}
if (Data.isDarkTheme && getDarkTheme() != -1) { if (Data.isDarkTheme && getDarkTheme() != -1) {
setTheme(getDarkTheme()); setTheme(getDarkTheme());
} }
@ -47,9 +51,7 @@ public abstract class FlavorActivity extends AppCompatActivity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
if (this instanceof Topic.Subscriber) { Topic.unsubscribe(this);
((Topic.Subscriber) this).unsubscribeTopics();
}
super.onDestroy(); super.onDestroy();
} }

View File

@ -6,7 +6,7 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
public class Fragment extends android.support.v4.app.Fragment { public class Fragment extends android.support.v4.app.Fragment implements Topic.AutoSubscriber {
public MagiskManager getApplication() { public MagiskManager getApplication() {
return Utils.getMagiskManager(getActivity()); return Utils.getMagiskManager(getActivity());
@ -15,16 +15,12 @@ public class Fragment extends android.support.v4.app.Fragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (this instanceof Topic.Subscriber) { Topic.subscribe(this);
((Topic.Subscriber) this).subscribeTopics();
}
} }
@Override @Override
public void onPause() { public void onPause() {
if (this instanceof Topic.Subscriber) { Topic.unsubscribe(this);
((Topic.Subscriber) this).unsubscribeTopics();
}
super.onPause(); super.onPause();
} }
@ -40,4 +36,9 @@ public class Fragment extends android.support.v4.app.Fragment {
public void runWithPermission(String[] permissions, Runnable callback) { public void runWithPermission(String[] permissions, Runnable callback) {
((Activity) requireActivity()).runWithPermission(permissions,callback); ((Activity) requireActivity()).runWithPermission(permissions,callback);
} }
@Override
public int[] getSubscribedTopics() {
return FlavorActivity.EMPTY_INT_ARRAY;
}
} }

View File

@ -87,7 +87,7 @@ public class LocaleManager {
} }
@Override @Override
protected void onPostExecute(Void aVoid) { protected void onPostExecute(Void aVoid) {
Data.MM().localeDone.publish(); Topic.publish(Topic.LOCAL_FETCH_DONE);
} }
} }
} }

View File

@ -1,99 +1,98 @@
package com.topjohnwu.magisk.utils; package com.topjohnwu.magisk.utils;
import java.lang.ref.WeakReference; import android.support.annotation.IntDef;
import java.util.ArrayList;
import java.util.List; import com.topjohnwu.magisk.Data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
public class Topic { public class Topic {
private static final int NON_INIT = 0; public static final int MAGISK_HIDE_DONE = 0;
private static final int PENDING = 1; public static final int RELOAD_ACTIVITY = 1;
private static final int PUBLISHED = 2; public static final int MODULE_LOAD_DONE = 2;
public static final int REPO_LOAD_DONE = 3;
public static final int UPDATE_CHECK_DONE = 4;
public static final int SNET_CHECK_DONE = 5;
public static final int LOCAL_FETCH_DONE = 6;
private int state = NON_INIT; @IntDef({MAGISK_HIDE_DONE, RELOAD_ACTIVITY, MODULE_LOAD_DONE, REPO_LOAD_DONE,
private List<WeakReference<Subscriber>> subscribers; UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCAL_FETCH_DONE})
private Object[] results; @Retention(RetentionPolicy.SOURCE)
public @interface TopicID {}
public Topic() { // We will not dynamically add topics, so use arrays instead of hash tables
subscribers = new SyncArrayList<>(); private static Store[] topicList = new Store[7];
}
public synchronized void subscribe(Subscriber sub) { public static void subscribe(Subscriber sub, @TopicID int... topics) {
subscribers.add(new WeakReference<>(sub)); for (int topic : topics) {
} if (topicList[topic] == null)
topicList[topic] = new Store();
public synchronized void unsubscribe() { topicList[topic].subscribers.add(sub);
subscribers = new SyncArrayList<>(); if (topicList[topic].published) {
} sub.onPublish(topic, topicList[topic].results);
}
public synchronized void unsubscribe(Subscriber sub) {
List<WeakReference<Subscriber>> subs = subscribers;
subscribers = new ArrayList<>();
for (WeakReference<Subscriber> subscriber : subs) {
if (subscriber.get() != null && subscriber.get() != sub)
subscribers.add(subscriber);
} }
} }
public void reset() { public static void subscribe(AutoSubscriber sub) {
state = NON_INIT; if (sub instanceof Subscriber)
results = null; subscribe((Subscriber) sub, sub.getSubscribedTopics());
} }
public boolean isPublished() { public static void unsubscribe(Subscriber sub, @TopicID int... topics) {
return state == PUBLISHED; for (int topic : topics) {
} if (topicList[topic] == null)
continue;
public void publish() { topicList[topic].subscribers.remove(sub);
publish(true);
}
public void publish(boolean record, Object... results) {
if (record)
state = PUBLISHED;
this.results = results;
// Snapshot
List<WeakReference<Subscriber>> subs = subscribers;
for (WeakReference<Subscriber> subscriber : subs) {
if (subscriber != null && subscriber.get() != null)
subscriber.get().onTopicPublished(this);
} }
} }
public Object[] getResults() { public static void unsubscribe(AutoSubscriber sub) {
return results; if (sub instanceof Subscriber)
unsubscribe((Subscriber) sub, sub.getSubscribedTopics());
} }
public boolean isPending() { public static void publish(@TopicID int topic, Object... results) {
return state == PENDING; publish(true, topic, results);
} }
public void setPending() { public static void publish(boolean persist, @TopicID int topic, Object... results) {
state = PENDING; if (topicList[topic] == null)
topicList[topic] = new Store();
if (persist) {
topicList[topic].results = results;
topicList[topic].published = true;
}
for (Subscriber sub : topicList[topic].subscribers) {
Data.mainHandler.post(() -> sub.onPublish(topic, results));
}
}
public static void reset(@TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
continue;
topicList[topic].published = false;
topicList[topic].results = null;
}
}
private static class Store {
boolean published = false;
Set<Subscriber> subscribers = new HashSet<>();
Object[] results;
} }
public interface Subscriber { public interface Subscriber {
default void subscribeTopics() { void onPublish(int topic, Object[] result);
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();
} }
private static class SyncArrayList<E> extends ArrayList<E> { public interface AutoSubscriber {
@Override @TopicID
public synchronized boolean add(E e) { int[] getSubscribedTopics();
return super.add(e);
}
} }
} }