diff --git a/app/build.gradle b/app/build.gradle index 913193b4a..442597aa7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,10 +38,7 @@ dependencies { compile 'com.github.d8ahazard:BroadcastTileSupportUpdate:master' compile 'com.jakewharton:butterknife:8.4.0' compile 'com.github.michalis-vitos:aFileChooser:master' - - - - + compile 'com.google.code.gson:gson:2.7' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' } diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java index 817b39714..623e25b0b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java @@ -7,7 +7,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; -import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,6 +14,7 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Utils; import java.io.File; @@ -112,7 +112,7 @@ public class MagiskFragment extends Fragment { new Utils.DownloadReceiver(getString(R.string.magisk)) { @Override public void task(File file) { - new Utils.FlashZIP(mContext, mName, file.getPath()).execute(); + new Async.FlashZIP(mContext, mName, file.getPath()).execute(); } }, Utils.magiskLink, "latest_magisk.zip"); diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java b/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java index 36085bf09..fe7ba2676 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java @@ -21,6 +21,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.topjohnwu.magisk.module.Module; +import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.WebWindow; @@ -84,8 +85,8 @@ public class ModulesAdapter extends RecyclerView.Adapter { recyclerView.setVisibility(View.GONE); - new Utils.LoadModules(getActivity()).execute(); + new Async.LoadModules(getActivity()).execute(); new updateUI().execute(); prefs.edit().putBoolean("ignoreUpdateAlerts", false).apply(); diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposAdapter.java b/app/src/main/java/com/topjohnwu/magisk/ReposAdapter.java index b9b86ca78..f3b35de70 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ReposAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/ReposAdapter.java @@ -21,6 +21,7 @@ import android.widget.TextView; import android.widget.Toast; import com.topjohnwu.magisk.module.Repo; +import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.WebWindow; @@ -91,9 +92,9 @@ public class ReposAdapter extends RecyclerView.Adapter prefs = PreferenceManager.getDefaultSharedPreferences(context); repo = mList.get(position); mHolder = holder; - mDonateUrl = repo.getmDonateUrl(); - mSupportUrl = repo.getmSupportUrl(); - mLogUrl = repo.getmLogUrl(); + mDonateUrl = repo.getDonateUrl(); + mSupportUrl = repo.getSupportUrl(); + mLogUrl = repo.getLogUrl(); mExpandedList = new ArrayList<>(mList.size()); for (int i = 0; i < mList.size(); i++) { mExpandedList.add(false); @@ -113,13 +114,10 @@ public class ReposAdapter extends RecyclerView.Adapter } mHolder.title.setText(titleString); - mHolder.versionName.setText(repo.getmVersion()); + mHolder.versionName.setText(repo.getVersion()); mHolder.description.setText(repo.getDescription()); - String authorString = this.context.getResources().getString(R.string.author) + " " + repo.getmAuthor(); + String authorString = this.context.getResources().getString(R.string.author) + " " + repo.getAuthor(); mHolder.author.setText(authorString); - String logUrl = repo.getmLogUrl(); - String supportUrl = repo.getmSupportUrl(); - String donateUrl = repo.getmDonateUrl(); if (prefs.contains("ignoreUpdateAlerts")) { ignoreAlertUpdate = prefs.getBoolean("ignoreUpdateAlerts",false); } @@ -135,7 +133,7 @@ public class ReposAdapter extends RecyclerView.Adapter mHolder.updateStatus.setText(repo.canUpdate() ? this.context.getResources().getString(R.string.module_update_available) : this.context.getResources().getString(R.string.module_up_to_date)); } - Log.d("Magisk", "ReposAdapter: Setting up info " + repo.getId() + " and " + repo.getDescription() + " and " + repo.getmVersion()); + Log.d("Magisk", "ReposAdapter: Setting up info " + repo.getId() + " and " + repo.getDescription() + " and " + repo.getVersion()); prefs = PreferenceManager.getDefaultSharedPreferences(context); mCanUpdate = prefs.getBoolean("repo-canUpdate_" + repo.getId(), false); @@ -150,23 +148,23 @@ public class ReposAdapter extends RecyclerView.Adapter @Override public void task(File file) { Log.d("Magisk", "Task firing"); - new Utils.FlashZIP(context, repo.getId(), file.toString()).execute(); + new Async.FlashZIP(context, repo.getId(), file.toString()).execute(); } }; String filename = repo.getId().replace(" ", "") + ".zip"; - Utils.downloadAndReceive(context, receiver, repo.getmZipUrl(), filename); + Utils.downloadAndReceive(context, receiver, repo.getZipUrl(), filename); } else { Toast.makeText(context, repo.getId() + " is already installed.", Toast.LENGTH_SHORT).show(); } } - if ((view.getId() == mHolder.changeLog.getId()) && (!repo.getmLogUrl().equals(""))) { - new WebWindow("Changelog", repo.getmLogUrl(),context); + if ((view.getId() == mHolder.changeLog.getId()) && (!repo.getLogUrl().equals(""))) { + new WebWindow("Changelog", repo.getLogUrl(),context); } - if ((view.getId() == mHolder.authorLink.getId()) && (!repo.getmSupportUrl().equals(""))) { - new WebWindow("Donate", repo.getmDonateUrl(),context); + if ((view.getId() == mHolder.authorLink.getId()) && (!repo.getSupportUrl().equals(""))) { + new WebWindow("Donate", repo.getDonateUrl(),context); } - if ((view.getId() == mHolder.supportLink.getId()) && (!repo.getmSupportUrl().equals(""))) { - new WebWindow("Support", repo.getmSupportUrl(),context); + if ((view.getId() == mHolder.supportLink.getId()) && (!repo.getSupportUrl().equals(""))) { + new WebWindow("Support", repo.getSupportUrl(),context); } }; mHolder.changeLog.setOnClickListener(oCl); diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java index 0342c2dc4..ef89500b7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java @@ -17,6 +17,7 @@ import android.widget.TextView; import com.topjohnwu.magisk.module.Repo; import com.topjohnwu.magisk.module.RepoHelper; +import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Utils; import java.io.File; @@ -111,14 +112,7 @@ public class ReposFragment extends Fragment { }; Log.d("Magisk","ReposFragment, LoadRepo called"); - mListRepos.clear(); - List magiskRepos = RepoHelper.listRepos(getActivity(), doReload, taskDelegate); - - for (Repo repo : magiskRepos) { - Log.d("Magisk", "ReposFragment: Adding repo from string " + repo.getId()); - mListRepos.add(repo); - } - + new Async.LoadRepos(getActivity()); } private void NotifyOfAlerts() { @@ -133,11 +127,11 @@ public class ReposFragment extends Fragment { @Override public void task(File file) { Log.d("Magisk", "Task firing"); - new Utils.FlashZIP(getActivity(), repo.getId(), file.toString()).execute(); + new Async.FlashZIP(getActivity(), repo.getId(), file.toString()).execute(); } }; String filename = repo.getId().replace(" ", "") + ".zip"; - Utils.downloadAndReceive(getActivity(), receiver, repo.getmZipUrl(), filename); + Utils.downloadAndReceive(getActivity(), receiver, repo.getZipUrl(), filename); break; diff --git a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java index ee678ec9d..34792bbc0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java @@ -24,6 +24,7 @@ import android.view.MenuItem; import android.view.View; import com.topjohnwu.magisk.module.RepoHelper; +import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.services.MonitorService; import com.topjohnwu.magisk.utils.Utils; @@ -71,16 +72,13 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView } - new Utils.Initialize(this).execute(); - new Utils.CheckUpdates(this).execute(); + Utils.init(this); + new Async.CheckUpdates(this).execute(); RepoHelper.TaskDelegate delegate = result -> { //Do a thing here when we get a result we want }; - new Utils.LoadModules(this).execute(); - new Utils.LoadRepos(this, true, delegate).execute(); - new Utils.LoadRepos(this, !prefs.contains("hasCachedRepos"), delegate).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - new Utils.LoadModules(getApplication()).execute(); - new Utils.LoadRepos(this, false, delegate).execute(); + new Async.LoadModules(this).execute(); + new Async.LoadRepos(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); setSupportActionBar(toolbar); diff --git a/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java b/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java index 707ee371e..9756aacd1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java @@ -7,16 +7,13 @@ import java.util.List; public abstract class BaseModule { - protected String mId, mName, mVersion, mDescription, mSupportUrl, mDonateUrl, mAuthor; + protected String mId, mName, mVersion, mAuthor, mDescription, mSupportUrl, mDonateUrl; protected boolean mIsCacheModule = false; - protected int mVersionCode = 0; - public BaseModule(List props) { - this(props.toArray(new String[props.size()])); - } + protected void parseProps(List props) { parseProps(props.toArray(new String[props.size()])); } - public BaseModule(String[] props) { + protected void parseProps(String[] props) { for (String line : props) { String[] prop = line.split("=", 2); if (prop.length != 2) { @@ -93,11 +90,11 @@ public abstract class BaseModule { return mVersionCode; } - public String getmDonateUrl() { + public String getDonateUrl() { return mDonateUrl; } - public String getmSupportUrl() { + public String getSupportUrl() { return mSupportUrl; } } diff --git a/app/src/main/java/com/topjohnwu/magisk/module/Module.java b/app/src/main/java/com/topjohnwu/magisk/module/Module.java index 2e3dc3d35..2724738fa 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Module.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Module.java @@ -16,9 +16,10 @@ public class Module extends BaseModule { private String mZipUrl, mLogUrl; private boolean mEnable, mRemove, mUpdateAvailable = false, mIsInstalled; - public Module(String path, Context context) { + public Module(Context context, String path) { - super(Utils.readFile(path + "/module.prop")); + super(); + parseProps(Utils.readFile(path + "/module.prop")); mRemoveFile = path + "/remove"; mDisableFile = path + "/disable"; diff --git a/app/src/main/java/com/topjohnwu/magisk/module/Repo.java b/app/src/main/java/com/topjohnwu/magisk/module/Repo.java index 16461758e..c30641ceb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Repo.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Repo.java @@ -15,254 +15,47 @@ import org.json.JSONObject; import java.util.Date; -public class Repo { - private String mBaseUrl; - private String mZipUrl; - private String mLogUrl; - private String mManifestUrl; - private String mVersion; - private String mName; - private String mDescription; - private String mAuthor; - public String mAuthorUrl; - private String mId; - private String mVersionCode; - private String mSupportUrl; - private String mDonateUrl; - private String lastUpdate; - private Context appContext; - private boolean mIsInstalled,mCanUpdate,mIsCacheModule; - - - public Repo(String manifestString, Context context) { - appContext = context; - ParseProps(manifestString); +public class Repo extends BaseModule { + protected String repoName, mLogUrl, mManifestUrl, mZipUrl; + protected Date mLastUpdate; + protected boolean mIsInstalled = false, mCanUpdate = false; + public Repo(Context context, String name, Date lastUpdate) { + repoName = name; + mLastUpdate = lastUpdate; + mLogUrl = context.getString(R.string.file_url, repoName, "changelog.txt"); + mManifestUrl = context.getString(R.string.file_url, repoName, "module.prop"); + mZipUrl = context.getString(R.string.zip_url, repoName); + update(); } - - - public Repo(String name, String url, Date updated, Context context) { - appContext = context; - this.mName = name; - this.mBaseUrl = url; - this.lastUpdate = updated.toString(); - - this.fetch(); - + public void update() { + String props = WebRequest.makeWebServiceCall(mManifestUrl, WebRequest.GET, true); + String lines[] = props.split("\\n"); + parseProps(lines); } - public Repo(String moduleName, String moduleDescription, String zipUrl, Date lastUpdated, Context context) { - appContext = context; - this.mZipUrl = zipUrl; - this.mDescription = moduleDescription; - this.mName = moduleName; - this.lastUpdate = lastUpdated.toString(); - this.fetch(); - - } - - public void fetch() { - - // Construct initial url for contents - String repoString = WebRequest.makeWebServiceCall(mBaseUrl + "/contents?access_token=" + Utils.procFile(appContext.getString(R.string.some_string), appContext), WebRequest.GET); - try { - JSONArray repoArray = new JSONArray(repoString); - for (int f = 0; f < repoArray.length(); f++) { - JSONObject jsonobject = repoArray.getJSONObject(f); - String name = jsonobject.getString("name"); - String url = jsonobject.getString("download_url").trim(); - Log.d("Magisk","Repo - checking object named " + name + " with value of " + url); - if (name.contains(".zip")) { - this.mZipUrl = url; - } - if (name.equals("module.prop")) { - this.mManifestUrl = url; - } - if (name.contains("log.txt")) { - Log.d("Magisk","Repo: Setting log URL for " + name + " of " + url); - this.mLogUrl = url; - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - - String manifestString = WebRequest.makeWebServiceCall(mManifestUrl, WebRequest.GET, true); - - if (ParseProps(manifestString)) { - PutProps(manifestString); - } - - } - - private void PutProps(String manifestString) { - manifestString = manifestString + "zipUrl=" + mZipUrl + "\nbaseUrl=" + mBaseUrl + "\nlogUrl=" + mLogUrl + "\nmanifestUrl=" + mManifestUrl; - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString("repo_" + mId, manifestString); - editor.putBoolean("hasCachedRepos", true); - editor.putString("updated_" + mId, this.lastUpdate); - editor.apply(); - } - private boolean ParseProps(String string) { - - if ((string.length() <= 1) | (!string.contains("id"))) { - return false; - } else { - String lines[] = string.split("\\n"); - for (String line : lines) { - if (line != "") { - String props[] = line.split("="); - switch (props[0]) { - case "versionCode": - this.mVersionCode = props[1]; - break; - case "name": - this.mName = props[1]; - break; - case "author": - this.mAuthor = props[1]; - break; - case "id": - this.mId = props[1]; - break; - case "version": - this.mVersion = props[1]; - break; - case "description": - this.mDescription = props[1]; - break; - case "donate": - this.mDonateUrl = props[1]; - break; - case "cacheModule": - this.mIsCacheModule = Boolean.valueOf(props[1]); - break; - case "support": - this.mSupportUrl = props[1]; - break; - case "logUrl": - this.mLogUrl = props[1]; - break; - case "donateUrl": - this.mDonateUrl = props[1]; - break; - case "zipUrl": - this.mZipUrl = props[1]; - break; - case "baseUrl": - this.mBaseUrl = props[1]; - break; - case "manifestUrl": - this.mManifestUrl = props[1]; - break; - default: - Log.d("Magisk", "Manifest string not recognized: " + props[0]); - break; - } - } - - } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); - if (prefs.contains("repo-isInstalled_" + this.mId)) { - mIsInstalled = prefs.getBoolean("repo-isInstalled_" + this.mId,false); - } - if (prefs.contains("repo-canUpdate_" + this.mId)) { - mCanUpdate = prefs.getBoolean("repo-canUpdate_" + this.mId,false); - } - if (prefs.contains("updated_" + this.mId)) { - lastUpdate = prefs.getString("updated_" + this.mId,""); - } - - - - return this.mId != null; - + public void update(Date lastUpdate) { + if (lastUpdate.after(mLastUpdate)) { + mLastUpdate = lastUpdate; + update(); } } - - - - - - - - public String getStringProperty(String mValue) { - switch (mValue) { - case "author": - return mAuthor; - case "id": - return mId; - case "version": - return mVersion; - case "description": - return mDescription; - case "supportUrl": - return mSupportUrl; - case "donateUrl": - return mDonateUrl; - case "baseeUrl": - return mBaseUrl; - case "zipUrl": - return mZipUrl; - default: - return null; - } - } - - public String getName() { - return mName; - } - - public String getmVersion() { - return mVersion; - } - - public int getmVersionCode() { - return Integer.valueOf(mVersionCode); - } - - public String getDescription() { - return mDescription; - } - - public String getId() { - return mId; - } - - public String getmZipUrl() { + public String getZipUrl() { return mZipUrl; } - public String getmBaseUrl() { - return mBaseUrl; - } - - public String getmLogUrl() { + public String getLogUrl() { return mLogUrl; } - - public String getmAuthor() { - return mAuthor; - } - - public String getmDonateUrl() { - return mDonateUrl; - } - - public String getmManifestUrl() { + public String getManifestUrl() { return mManifestUrl; } - public String getmSupportUrl() { - return mSupportUrl; - } - - public String getLastUpdate() { - return lastUpdate; + public Date getLastUpdate() { + return mLastUpdate; } public boolean isInstalled() { return mIsInstalled; } @@ -270,3 +63,258 @@ public class Repo { public boolean isCacheModule() { return mIsCacheModule; } } +//public class Repo { +// private String mBaseUrl; +// private String mZipUrl; +// private String mLogUrl; +// private String mManifestUrl; +// private String mVersion; +// private String mName; +// private String mDescription; +// private String mAuthor; +// public String mAuthorUrl; +// private String mId; +// private String mVersionCode; +// private String mSupportUrl; +// private String mDonateUrl; +// private String lastUpdate; +// private Context appContext; +// private boolean mIsInstalled,mCanUpdate,mIsCacheModule; +// +// +// public Repo(String manifestString, Context context) { +// appContext = context; +// ParseProps(manifestString); +// +// } +// +// +// +// public Repo(String name, String url, Date updated, Context context) { +// appContext = context; +// this.mName = name; +// this.mBaseUrl = url; +// this.lastUpdate = updated.toString(); +// +// this.fetch(); +// +// } +// +// public Repo(String moduleName, String moduleDescription, String zipUrl, Date lastUpdated, Context context) { +// appContext = context; +// this.mZipUrl = zipUrl; +// this.mDescription = moduleDescription; +// this.mName = moduleName; +// this.lastUpdate = lastUpdated.toString(); +// this.fetch(); +// +// } +// +// public void fetch() { +// +// // Construct initial url for contents +// String repoString = WebRequest.makeWebServiceCall(mBaseUrl + "/contents?access_token=" + Utils.procFile(appContext.getString(R.string.some_string), appContext), WebRequest.GET); +// try { +// JSONArray repoArray = new JSONArray(repoString); +// for (int f = 0; f < repoArray.length(); f++) { +// JSONObject jsonobject = repoArray.getJSONObject(f); +// String name = jsonobject.getString("name"); +// String url = jsonobject.getString("download_url").trim(); +// Log.d("Magisk","Repo - checking object named " + name + " with value of " + url); +// if (name.contains(".zip")) { +// this.mZipUrl = url; +// } +// if (name.equals("module.prop")) { +// this.mManifestUrl = url; +// } +// if (name.contains("log.txt")) { +// Log.d("Magisk","Repo: Setting log URL for " + name + " of " + url); +// this.mLogUrl = url; +// } +// } +// } catch (JSONException e) { +// e.printStackTrace(); +// } +// +// String manifestString = WebRequest.makeWebServiceCall(mManifestUrl, WebRequest.GET, true); +// +// if (ParseProps(manifestString)) { +// PutProps(manifestString); +// } +// +// } +// +// private void PutProps(String manifestString) { +// manifestString = manifestString + "zipUrl=" + mZipUrl + "\nbaseUrl=" + mBaseUrl + "\nlogUrl=" + mLogUrl + "\nmanifestUrl=" + mManifestUrl; +// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); +// SharedPreferences.Editor editor = prefs.edit(); +// editor.putString("repo_" + mId, manifestString); +// editor.putBoolean("hasCachedRepos", true); +// editor.putString("updated_" + mId, this.lastUpdate); +// editor.apply(); +// } +// private boolean ParseProps(String string) { +// +// if ((string.length() <= 1) | (!string.contains("id"))) { +// return false; +// } else { +// String lines[] = string.split("\\n"); +// for (String line : lines) { +// if (line != "") { +// String props[] = line.split("="); +// switch (props[0]) { +// case "versionCode": +// this.mVersionCode = props[1]; +// break; +// case "name": +// this.mName = props[1]; +// break; +// case "author": +// this.mAuthor = props[1]; +// break; +// case "id": +// this.mId = props[1]; +// break; +// case "version": +// this.mVersion = props[1]; +// break; +// case "description": +// this.mDescription = props[1]; +// break; +// case "donate": +// this.mDonateUrl = props[1]; +// break; +// case "cacheModule": +// this.mIsCacheModule = Boolean.valueOf(props[1]); +// break; +// case "support": +// this.mSupportUrl = props[1]; +// break; +// case "logUrl": +// this.mLogUrl = props[1]; +// break; +// case "donateUrl": +// this.mDonateUrl = props[1]; +// break; +// case "zipUrl": +// this.mZipUrl = props[1]; +// break; +// case "baseUrl": +// this.mBaseUrl = props[1]; +// break; +// case "manifestUrl": +// this.mManifestUrl = props[1]; +// break; +// default: +// Log.d("Magisk", "Manifest string not recognized: " + props[0]); +// break; +// } +// } +// +// } +// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); +// if (prefs.contains("repo-isInstalled_" + this.mId)) { +// mIsInstalled = prefs.getBoolean("repo-isInstalled_" + this.mId,false); +// } +// if (prefs.contains("repo-canUpdate_" + this.mId)) { +// mCanUpdate = prefs.getBoolean("repo-canUpdate_" + this.mId,false); +// } +// if (prefs.contains("updated_" + this.mId)) { +// lastUpdate = prefs.getString("updated_" + this.mId,""); +// } +// +// +// +// return this.mId != null; +// +// } +// } +// +// +// +// +// +// +// +// +// public String getStringProperty(String mValue) { +// switch (mValue) { +// case "author": +// return mAuthor; +// case "id": +// return mId; +// case "version": +// return mVersion; +// case "description": +// return mDescription; +// case "supportUrl": +// return mSupportUrl; +// case "donateUrl": +// return mDonateUrl; +// case "baseeUrl": +// return mBaseUrl; +// case "zipUrl": +// return mZipUrl; +// default: +// return null; +// } +// } +// +// public String getName() { +// return mName; +// } +// +// public String getmVersion() { +// return mVersion; +// } +// +// public int getmVersionCode() { +// return Integer.valueOf(mVersionCode); +// } +// +// public String getDescription() { +// return mDescription; +// } +// +// public String getId() { +// return mId; +// } +// +// public String getZipUrl() { +// return mZipUrl; +// } +// +// public String getmBaseUrl() { +// return mBaseUrl; +// } +// +// public String getLogUrl() { +// return mLogUrl; +// } +// +// +// public String getAuthor() { +// return mAuthor; +// } +// +// public String getDonateUrl() { +// return mDonateUrl; +// } +// +// public String getmManifestUrl() { +// return mManifestUrl; +// } +// +// public String getSupportUrl() { +// return mSupportUrl; +// } +// +// public String getLastUpdate() { +// return lastUpdate; +// } +// +// public boolean isInstalled() { return mIsInstalled; } +// public boolean canUpdate() { return mCanUpdate; } +// public boolean isCacheModule() { return mIsCacheModule; } +//} + diff --git a/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java b/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java index 304e179d3..e4f5faa0a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java @@ -2,11 +2,9 @@ package com.topjohnwu.magisk.module; import android.content.Context; import android.content.SharedPreferences; -import android.os.AsyncTask; -import android.preference.PreferenceManager; -import android.util.Log; -import android.widget.Toast; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.WebRequest; @@ -18,181 +16,76 @@ import org.json.JSONObject; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; -import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; public class RepoHelper { private static List repos = new ArrayList<>(); private static String TAG = "Magisk"; - public RepoHelper() { - } + private static final String file_key = "RepoMap"; + private static final String key = "repomap"; + public static HashMap repoMap; - public static List listRepos(Context context, boolean refresh, TaskDelegate delegate) { - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - if (!prefs.contains("hasCachedRepos") | refresh) { - Log.d(TAG, "RepoHelper: Building from web"); - new BuildFromWeb(delegate, context).execute(); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - String date = format.format(Calendar.getInstance().getTime()); - prefs.edit().putString("last_update", date).apply(); - } else { - Log.d(TAG, "RepoHelper: Building from cache"); - BuildFromCache(context); + public static void createRepoMap(Context context) { + Gson gson = new Gson(); + SharedPreferences prefs = context.getSharedPreferences(file_key, Context.MODE_PRIVATE); + String jsonString = prefs.getString(key, null); + if (jsonString != null) { + repoMap = gson.fromJson(jsonString, new TypeToken< HashMap >(){}.getType()); } - Collections.sort(repos, new CustomComparator()); - return repos; - } - - private static void BuildFromCache(Context activityContext) { - repos.clear(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activityContext); - Map map = prefs.getAll(); - for (Map.Entry entry : map.entrySet()) { - if (entry.getKey().contains("repo_")) { - String repoString = entry.getValue().toString().replace(""", "\""); - repos.add(new Repo(repoString, activityContext)); - } - } - } - - static class BuildFromWeb extends AsyncTask { - - private TaskDelegate delegate; - private SharedPreferences prefs; - private Context activityContext; - - public BuildFromWeb(TaskDelegate delegate, Context activityContext) { - this.delegate = delegate; - this.activityContext = activityContext; - prefs = PreferenceManager.getDefaultSharedPreferences(activityContext); + if (repoMap == null) { + repoMap = new HashMap<>(); } - @Override - protected void onPreExecute() { - super.onPreExecute(); - - } - - @Override - protected void onProgressUpdate(String... values) { - super.onProgressUpdate(values); - prefs.edit().putBoolean("ignoreUpdateAlerts", false).apply(); - Toast.makeText(activityContext, "Refreshing online modules", Toast.LENGTH_SHORT).show(); - - } - - @Override - protected Boolean doInBackground(String... params) { - publishProgress(); - - // Making a request to url and getting response - String token = activityContext.getString(R.string.some_string); - String url1 = activityContext.getString(R.string.url_main); - String jsonStr = WebRequest.makeWebServiceCall(url1 + Utils.procFile(token, activityContext), WebRequest.GET); - if (jsonStr != null && !jsonStr.isEmpty()) { - - try { - repos.clear(); - JSONArray jsonArray = new JSONArray(jsonStr); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonobject = jsonArray.getJSONObject(i); - String name = jsonobject.getString("name"); - String url = jsonobject.getString("url"); - String lastUpdate = jsonobject.getString("updated_at"); - String mId = ""; - String cacheUpdate = ""; - String manifestString = ""; - boolean doUpdate = true; - boolean hasCachedDate = false; - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - Date updatedDate; - Map map = prefs.getAll(); - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().toString().contains(url)) { - Log.d("Magisk", "RepoHelper: found matching URL"); - manifestString = entry.getValue().toString(); - String[] entryStrings = entry.getValue().toString().split("\n"); - for (String valueString : entryStrings) { - String[] valueSub = valueString.split("="); - if (valueSub[0].equals("id")) { - mId = valueSub[1]; - if (prefs.contains("updated_" + mId)) { - cacheUpdate = prefs.getString("updated_" + mId, ""); - hasCachedDate = true; - } - } - } - - } - } - try { - updatedDate = format.parse(lastUpdate); - Log.d("Magisk", "RepoHelper: Dates found, online is " + updatedDate + " and cached is " + cacheUpdate); - - if (hasCachedDate) { - - doUpdate = !cacheUpdate.equals(updatedDate.toString()); - Log.d("Magisk", "RepoHelper: DoUpdate is " + doUpdate); - } - - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return true; - } - if (!name.contains("Repo.github.io")) { - if (doUpdate) { - repos.add(new Repo(name, url, updatedDate, activityContext)); - Log.d(TAG, "RepoHelper: Trying to add new repo for online refresh - " + name); - } else { - repos.add(new Repo(manifestString, activityContext)); - Log.d(TAG, "RepoHelper: Trying to add new repo using manifestString of " + mId); - } - } - for (int f = 0; f < repos.size(); f++) { - repos.get(f).fetch(); - } + // Making a request to url and getting response + String token = context.getString(R.string.some_string); + String url1 = context.getString(R.string.url_main); + String jsonStr = WebRequest.makeWebServiceCall(url1 + Utils.procFile(token, context), WebRequest.GET); + if (jsonStr != null && !jsonStr.isEmpty()) { + try { + JSONArray jsonArray = new JSONArray(jsonStr); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jsonobject = jsonArray.getJSONObject(i); + String id = jsonobject.getString("description"); + String name = jsonobject.getString("name"); + String lastUpdate = jsonobject.getString("updated_at"); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + Date updatedDate; + try { + updatedDate = format.parse(lastUpdate); + } catch (ParseException e) { + continue; + } + Repo repo = repoMap.get(id); + if (repo == null) { + repoMap.put(id, new Repo(context, name, updatedDate)); + } else { + repo.update(updatedDate); } - } catch (JSONException e) { - e.printStackTrace(); } - return false; - } else { - return true; + } catch (JSONException e) { + e.printStackTrace(); } + jsonString = gson.toJson(repoMap); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(key, jsonString); + editor.apply(); } + } - protected void onPostExecute(Boolean apiFail) { - if (apiFail) { - Toast.makeText(activityContext, "GitHub API Limit reached, please try refreshing again in an hour.", Toast.LENGTH_LONG).show(); - } else { - Log.d("Magisk", "RepoHelper: postExecute fired"); - delegate.taskCompletionResult("Complete"); - BuildFromCache(activityContext); - - } - - } + public static List getSortedList() { + ArrayList list = new ArrayList<>(repoMap.values()); + Collections.sort(list, new Utils.ModuleComparator()); + return list; } public interface TaskDelegate { void taskCompletionResult(String result); } - public static class CustomComparator implements Comparator { - @Override - public int compare(Repo o1, Repo o2) { - return o1.getName().compareTo(o2.getName()); - } - } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Async.java b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java new file mode 100644 index 000000000..266e9c347 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java @@ -0,0 +1,374 @@ +package com.topjohnwu.magisk.utils; + +import android.app.ProgressDialog; +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.DocumentsContract; +import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.widget.Toast; + +import com.topjohnwu.magisk.ModulesFragment; +import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.ReposFragment; +import com.topjohnwu.magisk.module.Module; +import com.topjohnwu.magisk.module.RepoHelper; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collections; +import java.util.List; + +public class Async { + + public static class CheckUpdates extends AsyncTask { + + private Context mContext; + + public CheckUpdates(Context context) { + mContext = context; + } + + @Override + protected Void doInBackground(Void... voids) { + try { + HttpURLConnection c = (HttpURLConnection) new URL(Utils.UPDATE_JSON).openConnection(); + c.setRequestMethod("GET"); + c.setInstanceFollowRedirects(false); + c.setDoOutput(false); + c.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + JSONObject json = new JSONObject(sb.toString()); + JSONObject magisk = json.getJSONObject("magisk"); + JSONObject app = json.getJSONObject("app"); + JSONObject root = json.getJSONObject("root"); + + Utils.remoteMagiskVersion = magisk.getInt("versionCode"); + Utils.magiskLink = magisk.getString("link"); + Utils.magiskChangelog = magisk.getString("changelog"); + + Utils.remoteAppVersion = app.getInt("versionCode"); + Utils.appLink = app.getString("link"); + Utils.appChangelog = app.getString("changelog"); + + Utils.phhLink = root.getString("phh"); + Utils.supersuLink = root.getString("supersu"); + + } catch (IOException | JSONException ignored) { + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (Shell.rootAccess() && Utils.magiskVersion == -1) { + new AlertDialog.Builder(mContext) + .setTitle(R.string.no_magisk_title) + .setMessage(R.string.no_magisk_msg) + .setCancelable(true) + .setPositiveButton(R.string.download_install, (dialogInterface, i) -> { + new AlertDialog.Builder(mContext) + .setTitle(R.string.root_method_title) + .setItems(new String[]{mContext.getString(R.string.phh), mContext.getString(R.string.supersu)}, (dialogInterface1, root) -> { + Utils.DownloadReceiver rootReceiver; + String link, filename; + switch (root) { + case 0: + link = Utils.phhLink; + filename = "phhsu.zip"; + rootReceiver = new Utils.DownloadReceiver(mContext.getString(R.string.phh)) { + @Override + public void task(File file) { + new FlashZIP(mContext, mName, file.getPath()).execute(); + } + }; + break; + case 1: + link = Utils.supersuLink; + filename = "supersu.zip"; + rootReceiver = new Utils.DownloadReceiver(mContext.getString(R.string.supersu)) { + @Override + public void task(File file) { + new FlashZIP(mContext, mName, file.getPath()).execute(); + } + }; + break; + default: + rootReceiver = null; + link = filename = null; + } + Utils.DownloadReceiver magiskReceiver = new Utils.DownloadReceiver(mContext.getString(R.string.magisk)) { + @Override + public void task(File file) { + Context temp = mContext; + new FlashZIP(mContext, mName, file.getPath()) { + @Override + protected void done() { + Utils.downloadAndReceive(temp, rootReceiver, link, filename); + } + }.execute(); + } + }; + Utils.downloadAndReceive(mContext, magiskReceiver, Utils.magiskLink, "latest_magisk.zip"); + }) + .show(); + }) + .setNegativeButton(R.string.no_thanks, null) + .show(); + } else if (Shell.rootStatus == 2) { + new AlertDialog.Builder(mContext) + .setTitle(R.string.root_system) + .setMessage(R.string.root_system_msg) + .setCancelable(true) + .setPositiveButton(R.string.download_install, (dialogInterface, i) -> { + new AlertDialog.Builder(mContext) + .setTitle(R.string.root_method_title) + .setItems(new String[]{mContext.getString(R.string.phh), mContext.getString(R.string.supersu)}, (dialogInterface1, root) -> { + switch (root) { + case 0: + Utils.downloadAndReceive( + mContext, + new Utils.DownloadReceiver(mContext.getString(R.string.phh)) { + @Override + public void task(File file) { + new FlashZIP(mContext, mName, file.getPath()).execute(); + } + }, + Utils.phhLink, "phhsu.zip"); + break; + case 1: + Utils.downloadAndReceive( + mContext, + new Utils.DownloadReceiver(mContext.getString(R.string.supersu)) { + @Override + public void task(File file) { + new FlashZIP(mContext, mName, file.getPath()).execute(); + } + }, + Utils.supersuLink, "supersu.zip"); + break; + } + }) + .show(); + }) + .setNegativeButton(R.string.no_thanks, null) + .show(); + } + } + } + + public static class LoadModules extends AsyncTask { + + private Context mContext; + + public LoadModules(Context context) { + mContext = context; + } + + @Override + protected Void doInBackground(Void... voids) { + ModulesFragment.listModules.clear(); + List magisk = Utils.getModList(Utils.MAGISK_PATH); + Log.d("Magisk", "Utils: Reload called, loading modules"); + List magiskCache = Utils.getModList(Utils.MAGISK_CACHE_PATH); + + for (String mod : magisk) { + Log.d("Magisk", "Utils: Adding module from string " + mod); + ModulesFragment.listModules.add(new Module(mContext, mod)); + } + + for (String mod : magiskCache) { + Log.d("Magisk", "Utils: Adding cache module from string " + mod); + Module cacheMod = new Module(mContext, mod); + // Prevent people forgot to change module.prop + cacheMod.setCache(); + ModulesFragment.listModules.add(cacheMod); + } + + Collections.sort(ModulesFragment.listModules, new Utils.ModuleComparator()); + + return null; + } + + } + + public static class LoadRepos extends AsyncTask { + + private Context mContext; + private boolean doReload; + private RepoHelper.TaskDelegate mTaskDelegate; + + public LoadRepos(Context context) { + mContext = context; + } + + @Override + protected Void doInBackground(Void... voids) { + ReposFragment.mListRepos.clear(); + RepoHelper.createRepoMap(mContext); + ReposFragment.mListRepos = RepoHelper.getSortedList(); + return null; + } + + } + + public static class FlashZIP extends AsyncTask { + + private String mPath, mName; + private Uri mUri; + private ProgressDialog progress; + private File mFile; + private Context mContext; + private List ret; + private boolean deleteFileAfter; + + public FlashZIP(Context context, String name, String path) { + mContext = context; + mName = name; + mPath = path; + deleteFileAfter = false; + } + + public FlashZIP(Context context, Uri uRi) { + mContext = context; + mUri = uRi; + deleteFileAfter = true; + String file = ""; + final String docId = DocumentsContract.getDocumentId(mUri); + + Log.d("Magisk", "Utils: FlashZip Running, " + docId + " and " + mUri.toString()); + if (docId.contains(":")) + mName = docId.split(":")[1]; + else mName = docId; + if (mName.contains("/")) + mName = mName.substring(mName.lastIndexOf('/') + 1); + if (mName.contains(".zip")) { + file = mContext.getFilesDir() + "/" + mName; + Log.d("Magisk", "Utils: FlashZip running for uRI " + mUri.toString()); + } else { + Log.e("Magisk", "Utils: error parsing Zipfile " + mUri.getPath()); + this.cancel(true); + } + ContentResolver contentResolver = mContext.getContentResolver(); + //contentResolver.takePersistableUriPermission(mUri, flags); + try { + InputStream in = contentResolver.openInputStream(mUri); + Log.d("Magisk", "Firing inputStream"); + mFile = createFileFromInputStream(in, file, mContext); + if (mFile != null) { + mPath = mFile.getPath(); + Log.d("Magisk", "Utils: Mpath is " + mPath); + } else { + Log.e("Magisk", "Utils: error creating file " + mUri.getPath()); + this.cancel(true); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + // TODO handle non-primary volumes + + } + + private static File createFileFromInputStream(InputStream inputStream, String fileName, Context context) { + + try { + File f = new File(fileName); + f.setWritable(true, false); + OutputStream outputStream = new FileOutputStream(f); + byte buffer[] = new byte[1024]; + int length; + + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + + outputStream.close(); + inputStream.close(); + Log.d("Magisk", "Holy balls, I think it worked. File is " + f.getPath()); + return f; + + } catch (IOException e) { + System.out.println("error in creating a file"); + e.printStackTrace(); + } + + return null; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + + progress = ProgressDialog.show(mContext, mContext.getString(R.string.zip_install_progress_title), mContext.getString(R.string.zip_install_progress_msg, mName)); + } + + @Override + protected Boolean doInBackground(Void... voids) { + if (mPath != null) { + Log.e("Magisk", "Utils: Error, flashZIP called without a valid zip file to flash."); + progress.dismiss(); + this.cancel(true); + return false; + } + if (!Shell.rootAccess()) { + return false; + } else { + ret = Shell.su( + "rm -rf /dev/tmp", + "mkdir -p /dev/tmp", + "cp -af " + mPath + " /dev/tmp/install.zip", + "unzip -o /dev/tmp/install.zip META-INF/com/google/android/* -d /dev/tmp", + "BOOTMODE=true sh /dev/tmp/META-INF/com/google/android/update-binary dummy 1 /dev/tmp/install.zip", + "if [ $? -eq 0 ]; then echo true; else echo false; fi" + ); + return ret != null && Boolean.parseBoolean(ret.get(ret.size() - 1)); + } + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (deleteFileAfter) { + Shell.su("rm -rf " + mPath); + Log.d("Magisk", "Utils: Deleting file " + mPath); + } + progress.dismiss(); + if (!result) { + Toast.makeText(mContext, mContext.getString(R.string.manual_install, mPath), Toast.LENGTH_LONG).show(); + return; + } + done(); + } + + protected void done() { + new AlertDialog.Builder(mContext) + .setTitle(R.string.reboot_title) + .setMessage(R.string.reboot_msg) + .setPositiveButton(R.string.reboot, (dialogInterface1, i) -> Shell.su("reboot")) + .setNegativeButton(R.string.no_thanks, null) + .show(); + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4801921f..d7d7e9e82 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,4 +105,6 @@ GTYybRBTYf5his9kQ16ZNO7qgkBJ/5MyVe4CGceAOIoXgSnnk8FTd4F1dE9p5Eus Downloads https://api.github.com/orgs/Magisk-Modules-Repo/repos?access_token= + https://raw.githubusercontent.com/Magisk-Modules-Repo/%1$s/master/%2$s + https://github.com/Magisk-Modules-Repo/%1$s/archive/master.zip