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

View File

@ -80,13 +80,13 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
}
@Override
public void onTopicPublished(Topic topic) {
mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(null);
public int[] getSubscribedTopics() {
return new int[] {Topic.MAGISK_HIDE_DONE};
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getApplication().magiskHideDone };
public void onPublish(int topic, Object[] result) {
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.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.ContainerApp;
import com.topjohnwu.superuser.Shell;
@ -20,15 +19,6 @@ import java.util.Map;
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
public boolean hasInit = false;

View File

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

View File

@ -4,6 +4,7 @@ import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
@ -49,7 +50,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
@Nullable
@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);
unbinder = ButterKnife.bind(this, view);
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;
}
@Override
public void onTopicPublished(Topic topic) {
updateUI();
public int[] getSubscribedTopics() {
return new int[] {Topic.MODULE_LOAD_DONE};
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getApplication().moduleLoadDone };
public void onPublish(int topic, Object[] result) {
updateUI();
}
@Override

View File

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

View File

@ -73,17 +73,18 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
}
@Override
public void onTopicPublished(Topic topic) {
recreate();
public int[] getSubscribedTopics() {
return new int[] {Topic.RELOAD_ACTIVITY};
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getMagiskManager().reloadActivity };
public void onPublish(int topic, Object[] result) {
recreate();
}
public static class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber {
private SharedPreferences prefs;
private PreferenceScreen prefScreen;
@ -229,14 +230,14 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
prefs.registerOnSharedPreferenceChangeListener(this);
subscribeTopics();
Topic.subscribe(this);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onDestroyView() {
prefs.unregisterOnSharedPreferenceChangeListener(this);
unsubscribeTopics();
Topic.unsubscribe(this);
super.onDestroyView();
}
@ -246,7 +247,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
switch (key) {
case Const.Key.DARK_THEME:
Data.isDarkTheme = prefs.getBoolean(key, false);
mm.reloadActivity.publish(false);
Topic.publish(false, Topic.RELOAD_ACTIVITY);
return;
case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) {
@ -283,7 +284,7 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
break;
case Const.Key.LOCALE:
LocaleManager.setLocale();
mm.reloadActivity.publish(false);
Topic.publish(false, Topic.RELOAD_ACTIVITY);
break;
case Const.Key.UPDATE_CHANNEL:
new CheckUpdates().exec();
@ -314,13 +315,13 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
}
@Override
public void onTopicPublished(Topic topic) {
public void onPublish(int topic, Object[] result) {
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
}
@Override
public Topic[] getSubscription() {
return new Topic[] { mm.localeDone };
public int[] getSubscribedTopics() {
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.R;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@ -152,7 +153,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
@Override
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.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.WebService;
import com.topjohnwu.superuser.Shell;
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)
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
(ISafetyNetHelper.Callback) code ->
Data.MM().safetyNetDone.publish(false, code));
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() != Const.SNET_VER) {
throw new Exception();
}
@ -79,7 +80,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
helper.attest();
} else {
e.printStackTrace();
Data.MM().safetyNetDone.publish(false, -1);
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
}
super.onPostExecute(e);
}

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.ReposFragment;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
@ -49,8 +50,8 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
private ExecutorService threadPool;
public UpdateRepos(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
mm = Data.MM();
mm.repoLoadDone.reset();
forceUpdate = force;
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
}
@ -147,11 +148,6 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
ReposFragment.adapter.notifyDBChanged();
}
@Override
protected void onPreExecute() {
mm.repoLoadDone.setPending();
}
@Override
protected Void doInBackground(Void... voids) {
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
@ -186,7 +182,7 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
@Override
protected void onPostExecute(Void v) {
mm.repoLoadDone.publish();
Topic.publish(Topic.REPO_LOAD_DONE);
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.Topic;
public abstract class FlavorActivity extends AppCompatActivity {
public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
private ActivityResultListener activityResultListener;
static int[] EMPTY_INT_ARRAY = new int[0];
public FlavorActivity() {
super();
@ -25,6 +26,11 @@ public abstract class FlavorActivity extends AppCompatActivity {
applyOverrideConfiguration(configuration);
}
@Override
public int[] getSubscribedTopics() {
return EMPTY_INT_ARRAY;
}
@StyleRes
public int getDarkTheme() {
return -1;
@ -37,9 +43,7 @@ public abstract class FlavorActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (this instanceof Topic.Subscriber) {
((Topic.Subscriber) this).subscribeTopics();
}
Topic.subscribe(this);
if (Data.isDarkTheme && getDarkTheme() != -1) {
setTheme(getDarkTheme());
}
@ -47,9 +51,7 @@ public abstract class FlavorActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
if (this instanceof Topic.Subscriber) {
((Topic.Subscriber) this).unsubscribeTopics();
}
Topic.unsubscribe(this);
super.onDestroy();
}

View File

@ -6,7 +6,7 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic;
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() {
return Utils.getMagiskManager(getActivity());
@ -15,16 +15,12 @@ public class Fragment extends android.support.v4.app.Fragment {
@Override
public void onResume() {
super.onResume();
if (this instanceof Topic.Subscriber) {
((Topic.Subscriber) this).subscribeTopics();
}
Topic.subscribe(this);
}
@Override
public void onPause() {
if (this instanceof Topic.Subscriber) {
((Topic.Subscriber) this).unsubscribeTopics();
}
Topic.unsubscribe(this);
super.onPause();
}
@ -40,4 +36,9 @@ public class Fragment extends android.support.v4.app.Fragment {
public void runWithPermission(String[] permissions, Runnable 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
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;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import android.support.annotation.IntDef;
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 {
private static final int NON_INIT = 0;
private static final int PENDING = 1;
private static final int PUBLISHED = 2;
public static final int MAGISK_HIDE_DONE = 0;
public static final int RELOAD_ACTIVITY = 1;
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;
private List<WeakReference<Subscriber>> subscribers;
private Object[] results;
@IntDef({MAGISK_HIDE_DONE, RELOAD_ACTIVITY, MODULE_LOAD_DONE, REPO_LOAD_DONE,
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCAL_FETCH_DONE})
@Retention(RetentionPolicy.SOURCE)
public @interface TopicID {}
public Topic() {
subscribers = new SyncArrayList<>();
// We will not dynamically add topics, so use arrays instead of hash tables
private static Store[] topicList = new Store[7];
public static void subscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
topicList[topic] = new Store();
topicList[topic].subscribers.add(sub);
if (topicList[topic].published) {
sub.onPublish(topic, topicList[topic].results);
}
public synchronized void subscribe(Subscriber sub) {
subscribers.add(new WeakReference<>(sub));
}
public synchronized void unsubscribe() {
subscribers = new SyncArrayList<>();
}
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() {
state = NON_INIT;
results = null;
public static void subscribe(AutoSubscriber sub) {
if (sub instanceof Subscriber)
subscribe((Subscriber) sub, sub.getSubscribedTopics());
}
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 (WeakReference<Subscriber> subscriber : subs) {
if (subscriber != null && subscriber.get() != null)
subscriber.get().onTopicPublished(this);
public static void unsubscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
continue;
topicList[topic].subscribers.remove(sub);
}
}
public Object[] getResults() {
return results;
public static void unsubscribe(AutoSubscriber sub) {
if (sub instanceof Subscriber)
unsubscribe((Subscriber) sub, sub.getSubscribedTopics());
}
public boolean isPending() {
return state == PENDING;
public static void publish(@TopicID int topic, Object... results) {
publish(true, topic, results);
}
public void setPending() {
state = PENDING;
public static void publish(boolean persist, @TopicID int topic, Object... results) {
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 {
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();
void onPublish(int topic, Object[] result);
}
private static class SyncArrayList<E> extends ArrayList<E> {
@Override
public synchronized boolean add(E e) {
return super.add(e);
}
public interface AutoSubscriber {
@TopicID
int[] getSubscribedTopics();
}
}