diff --git a/app/src/main/java/com/topjohnwu/magisk/BaseModuleFragment.java b/app/src/main/java/com/topjohnwu/magisk/BaseModuleFragment.java deleted file mode 100644 index e5fe56942..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/BaseModuleFragment.java +++ /dev/null @@ -1,390 +0,0 @@ -package com.topjohnwu.magisk; - -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.RecyclerView; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.topjohnwu.magisk.module.Module; -import com.topjohnwu.magisk.module.RepoHelper; -import com.topjohnwu.magisk.utils.Shell; -import com.topjohnwu.magisk.utils.Utils; -import com.topjohnwu.magisk.utils.WebWindow; - -import java.io.File; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; - -public abstract class BaseModuleFragment extends Fragment { - @BindView(R.id.swipeRefreshLayout) - SwipeRefreshLayout mSwipeRefreshLayout; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - @BindView(R.id.empty_rv) - TextView emptyTv; - - private RepoHelper.TaskDelegate mDelegate; - private SharedPreferences prefs; - - public BaseModuleFragment SetDelegate(RepoHelper.TaskDelegate delegate) { - mDelegate = delegate; - return null; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View viewMain = inflater.inflate(R.layout.single_module_fragment, container, false); - - - ButterKnife.bind(this, viewMain); - - - prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - mSwipeRefreshLayout.setOnRefreshListener(() -> { - - mDelegate.taskCompletionResult("OK"); - prefs.edit().putBoolean("ignoreUpdateAlerts", false).apply(); - - - }); - prefs.registerOnSharedPreferenceChangeListener((sharedPreferences, s) -> { - if (s.contains("updated")) { - viewMain.invalidate(); - viewMain.requestLayout(); - - } - }); - if (listModules().size() == 0) { - emptyTv.setVisibility(View.VISIBLE); - recyclerView.setVisibility(View.GONE); - - return viewMain; - } - - recyclerView.setAdapter(new ModulesAdapter(listModules(), (chk, position) -> { - // On Checkbox change listener - CheckBox chbox = (CheckBox) chk; - - if (!chbox.isChecked()) { - listModules().get(position).createDisableFile(); - Snackbar.make(chk, R.string.disable_file_created, Snackbar.LENGTH_SHORT).show(); - } else { - listModules().get(position).removeDisableFile(); - Snackbar.make(chk, R.string.disable_file_removed, Snackbar.LENGTH_SHORT).show(); - } - }, (deleteBtn, position) -> { - // On delete button click listener - - listModules().get(position).createRemoveFile(); - Snackbar.make(deleteBtn, R.string.remove_file_created, Snackbar.LENGTH_SHORT).show(); - }, (undeleteBtn, position) -> { - // On undelete button click listener - - listModules().get(position).deleteRemoveFile(); - Snackbar.make(undeleteBtn, R.string.remove_file_deleted, Snackbar.LENGTH_SHORT).show(); - })); - - - return viewMain; - } - - - protected abstract List listModules(); - - public class ModulesAdapter extends RecyclerView.Adapter { - - private final List mList; - private final List mListToUpdate = new ArrayList<>(); - List mExpandedList; - @BindView(R.id.expand_layout) - LinearLayout expandedLayout; - private View viewMain; - private Context context; - private final Utils.ItemClickListener chboxListener; - private final Utils.ItemClickListener deleteBtnListener; - private final Utils.ItemClickListener unDeleteBtnListener; - private boolean alertUpdate, ignoreAlertUpdate; - - public ModulesAdapter(List list, Utils.ItemClickListener chboxListener, Utils.ItemClickListener deleteBtnListener, Utils.ItemClickListener undeleteBtnListener) { - alertUpdate = false; - this.mList = list; - mExpandedList = new ArrayList<>(mList.size()); - for (int i = 0; i < mList.size(); i++) { - mExpandedList.add(false); - if (listModules().get(i).isUpdateAvailable()) { - alertUpdate = true; - mListToUpdate.add(listModules().get(i)); - } - } - this.chboxListener = chboxListener; - this.deleteBtnListener = deleteBtnListener; - this.unDeleteBtnListener = undeleteBtnListener; - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - viewMain = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false); - context = parent.getContext(); - ButterKnife.bind(this, viewMain); - return new ViewHolder(viewMain); - } - - @Override - public void onBindViewHolder(final ViewHolder holder, int position) { - final Module module = mList.get(position); - Log.d("Magisk", "ModulesAdapter: Trying set up bindview from list pos " + position + " and " + module.getName()); - Log.d("Magisk", "BaseModuleFragment: Log ID is " + module.getmLogUrl()); - holder.title.setText(module.getName()); - holder.versionName.setText(module.getVersion()); - holder.description.setText(module.getDescription()); - holder.author.setText(module.getAuthor()); - String logUrl = module.getmLogUrl(); - String supportUrl = module.getmSupportUrl(); - String donateUrl = module.getmDonateUrl(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (prefs.contains("ignoreUpdateAlerts")) { - ignoreAlertUpdate = prefs.getBoolean("ignoreUpdateAlerts", false); - } - if (prefs.contains("repo-canUpdate_" + module.getId())) { - if (prefs.getBoolean("repo-canUpdate_" + module.getId(), false)) { - holder.updateStatus.setText(R.string.module_update_available); - holder.updateStatus.setVisibility(View.VISIBLE); - } else { - holder.updateStatus.setVisibility(View.GONE); - } - - if (alertUpdate && !ignoreAlertUpdate) { - Iterator iterRepo = mListToUpdate.iterator(); - while (iterRepo.hasNext()) { - Module mModule = iterRepo.next(); - DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - Utils.DownloadReceiver receiver = new Utils.DownloadReceiver() { - @Override - public void task(File file) { - Log.d("Magisk", "Task firing"); - new Utils.FlashZIP(context, mModule.getId(), file.toString()).execute(); - } - }; - String filename = mModule.getId().replace(" ", "") + ".zip"; - Utils.downloadAndReceive(context, receiver, mModule.getmZipUrl(), filename); - - break; - - case DialogInterface.BUTTON_NEGATIVE: - ignoreAlertUpdate = true; - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean("ignoreUpdateAlerts", ignoreAlertUpdate); - editor.apply(); - break; - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage("An update is available for " + mModule.getName() + ". Would you like to install it?").setPositiveButton("Yes", dialogClickListener) - .setNegativeButton("No", dialogClickListener).show(); - mListToUpdate.remove(mModule); - } - } - } - - View.OnClickListener oCl = view -> { - if (view.getId() == holder.changeLog.getId()) { - - new WebWindow("Changelog", module.getmLogUrl(), context); - } - if (view.getId() == holder.authorLink.getId()) { - new WebWindow("Donate", module.getmDonateUrl(), context); - } - if (view.getId() == holder.supportLink.getId()) { - new WebWindow("Support", module.getmSupportUrl(), context); - } - }; - - holder.authorLink.setOnClickListener(oCl); - holder.changeLog.setOnClickListener(oCl); - holder.supportLink.setOnClickListener(oCl); - holder.checkBox.setChecked(module.isEnabled()); - holder.checkBox.setOnCheckedChangeListener((compoundButton, b) -> chboxListener.onItemClick(compoundButton, holder.getAdapterPosition())); - - holder.delete.setOnClickListener(view -> { - if (module.willBeRemoved()) { - unDeleteBtnListener.onItemClick(holder.delete, holder.getAdapterPosition()); - } else { - deleteBtnListener.onItemClick(holder.delete, holder.getAdapterPosition()); - } - - updateDeleteButton(holder, module); - }); - - updateDeleteButton(holder, module); - } - - private void updateDeleteButton(ViewHolder holder, Module module) { - holder.warning.setVisibility(module.willBeRemoved() ? View.VISIBLE : View.GONE); - - if (module.willBeRemoved()) { - holder.delete.setImageResource(R.drawable.ic_undelete); - } else { - holder.delete.setImageResource(R.drawable.ic_delete); - } - } - - @Override - public int getItemCount() { - return mList.size(); - } - - class ViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.title) - TextView title; - - @BindView(R.id.version_name) - TextView versionName; - @BindView(R.id.description) - TextView description; - @BindView(R.id.warning) - TextView warning; - @BindView(R.id.checkbox) - CheckBox checkBox; - @BindView(R.id.author) - TextView author; - @BindView(R.id.updateStatus) - TextView updateStatus; - @BindView(R.id.delete) - ImageView delete; - @BindView(R.id.changeLog) - ImageView changeLog; - @BindView(R.id.authorLink) - ImageView authorLink; - @BindView(R.id.supportLink) - ImageView supportLink; - @BindView(R.id.expand_layout) - LinearLayout expandLayout; - private ValueAnimator mAnimator; - private int mMeasuredHeight; - - public ViewHolder(View itemView) { - super(itemView); - WindowManager windowmanager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - ButterKnife.bind(this, itemView); - DisplayMetrics dimension = new DisplayMetrics(); - windowmanager.getDefaultDisplay().getMetrics(dimension); - - expandLayout.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - - - @Override - public boolean onPreDraw() { - expandLayout.getViewTreeObserver().removeOnPreDrawListener(this); - expandLayout.setVisibility(View.GONE); - final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - expandLayout.measure(widthSpec, heightSpec); - mAnimator = slideAnimator(0, expandLayout.getMeasuredHeight()); - return true; - } - }); - - viewMain.setOnClickListener(view -> { - int position = getAdapterPosition(); - Log.d("Magisk", "ReposFragment: CLICK. " + position + " and " + mExpandedList.get(position)); - - if (mExpandedList.get(position)) { - collapse(expandLayout); - } else { - expand(expandLayout); - } - mExpandedList.set(position, !mExpandedList.get(position)); - - }); - if (!Shell.rootAccess()) { - checkBox.setEnabled(false); - delete.setEnabled(false); - } - } - - private void expand(View view) { - - // set Visible - - - Log.d("Magisk", "ReposFragment: Expand anim called " + mMeasuredHeight + " and " + view.getId()); - view.setVisibility(View.VISIBLE); - mAnimator.start(); - } - - private void collapse(View view) { - int finalHeight = view.getHeight(); - ValueAnimator mAnimator = slideAnimator(finalHeight, 0); - Log.d("Magisk", "ReposFragment: Collapse anim called " + finalHeight + " and " + view.getId()); - - mAnimator.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationEnd(Animator animator) { - // Height=0, but it set visibility to GONE - view.setVisibility(View.GONE); - } - - @Override - public void onAnimationStart(Animator animator) { - } - - @Override - public void onAnimationCancel(Animator animator) { - } - - @Override - public void onAnimationRepeat(Animator animator) { - } - }); - mAnimator.start(); - } - - private ValueAnimator slideAnimator(int start, int end) { - - ValueAnimator animator = ValueAnimator.ofInt(start, end); - - animator.addUpdateListener(valueAnimator -> { - // Update Height - int value = (Integer) valueAnimator.getAnimatedValue(); - - ViewGroup.LayoutParams layoutParams = expandLayout - .getLayoutParams(); - layoutParams.height = value; - expandLayout.setLayoutParams(layoutParams); - }); - return animator; - } - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java b/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java new file mode 100644 index 000000000..36085bf09 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ModulesAdapter.java @@ -0,0 +1,307 @@ +package com.topjohnwu.magisk; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.RecyclerView; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.topjohnwu.magisk.module.Module; +import com.topjohnwu.magisk.utils.Shell; +import com.topjohnwu.magisk.utils.Utils; +import com.topjohnwu.magisk.utils.WebWindow; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class ModulesAdapter extends RecyclerView.Adapter { + + private final List mList; + private final List mListToUpdate = new ArrayList<>(); + List mExpandedList; + @BindView(R.id.expand_layout) + LinearLayout expandedLayout; + private View viewMain; + private Context context; + private final Utils.ItemClickListener chboxListener; + private final Utils.ItemClickListener deleteBtnListener; + private final Utils.ItemClickListener unDeleteBtnListener; + private boolean alertUpdate, ignoreAlertUpdate; + + public ModulesAdapter(List list, Utils.ItemClickListener chboxListener, Utils.ItemClickListener deleteBtnListener, Utils.ItemClickListener undeleteBtnListener) { + alertUpdate = false; + this.mList = list; + mExpandedList = new ArrayList<>(mList.size()); + for (int i = 0; i < mList.size(); i++) { + mExpandedList.add(false); + if (mList.get(i).isUpdateAvailable()) { + alertUpdate = true; + mListToUpdate.add(mList.get(i)); + } + } + this.chboxListener = chboxListener; + this.deleteBtnListener = deleteBtnListener; + this.unDeleteBtnListener = undeleteBtnListener; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + viewMain = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false); + context = parent.getContext(); + ButterKnife.bind(this, viewMain); + return new ViewHolder(viewMain); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + final Module module = mList.get(position); + Log.d("Magisk", "ModulesAdapter: Trying set up bindview from list pos " + position + " and " + module.getName()); + Log.d("Magisk", "ModulesFragment: Log ID is " + module.getmLogUrl()); + if (module.isCache()) + holder.title.setText("[Cache] " + module.getName()); + else + holder.title.setText(module.getName()); + holder.versionName.setText(module.getVersion()); + holder.description.setText(module.getDescription()); + holder.author.setText(module.getAuthor()); + String logUrl = module.getmLogUrl(); + String supportUrl = module.getmSupportUrl(); + String donateUrl = module.getmDonateUrl(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (prefs.contains("ignoreUpdateAlerts")) { + ignoreAlertUpdate = prefs.getBoolean("ignoreUpdateAlerts", false); + } + if (prefs.contains("repo-canUpdate_" + module.getId())) { + if (prefs.getBoolean("repo-canUpdate_" + module.getId(), false)) { + holder.updateStatus.setText(R.string.module_update_available); + holder.updateStatus.setVisibility(View.VISIBLE); + } else { + holder.updateStatus.setVisibility(View.GONE); + } + + if (alertUpdate && !ignoreAlertUpdate) { + Iterator iterRepo = mListToUpdate.iterator(); + while (iterRepo.hasNext()) { + Module mModule = iterRepo.next(); + DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + Utils.DownloadReceiver receiver = new Utils.DownloadReceiver() { + @Override + public void task(File file) { + Log.d("Magisk", "Task firing"); + new Utils.FlashZIP(context, mModule.getId(), file.toString()).execute(); + } + }; + String filename = mModule.getId().replace(" ", "") + ".zip"; + Utils.downloadAndReceive(context, receiver, mModule.getmZipUrl(), filename); + + break; + + case DialogInterface.BUTTON_NEGATIVE: + ignoreAlertUpdate = true; + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("ignoreUpdateAlerts", ignoreAlertUpdate); + editor.apply(); + break; + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage("An update is available for " + mModule.getName() + ". Would you like to install it?").setPositiveButton("Yes", dialogClickListener) + .setNegativeButton("No", dialogClickListener).show(); + mListToUpdate.remove(mModule); + } + } + } + + View.OnClickListener oCl = view -> { + if (view.getId() == holder.changeLog.getId()) { + new WebWindow("Changelog", module.getmLogUrl(), context); + } + if (view.getId() == holder.authorLink.getId()) { + new WebWindow("Donate", module.getmDonateUrl(), context); + } + if (view.getId() == holder.supportLink.getId()) { + new WebWindow("Support", module.getmSupportUrl(), context); + } + }; + + holder.authorLink.setOnClickListener(oCl); + holder.changeLog.setOnClickListener(oCl); + holder.supportLink.setOnClickListener(oCl); + holder.checkBox.setChecked(module.isEnabled()); + holder.checkBox.setOnCheckedChangeListener((compoundButton, b) -> chboxListener.onItemClick(compoundButton, holder.getAdapterPosition())); + + holder.delete.setOnClickListener(view -> { + if (module.willBeRemoved()) { + unDeleteBtnListener.onItemClick(holder.delete, holder.getAdapterPosition()); + } else { + deleteBtnListener.onItemClick(holder.delete, holder.getAdapterPosition()); + } + + updateDeleteButton(holder, module); + }); + + updateDeleteButton(holder, module); + } + + private void updateDeleteButton(ViewHolder holder, Module module) { + holder.warning.setVisibility(module.willBeRemoved() ? View.VISIBLE : View.GONE); + + if (module.willBeRemoved()) { + holder.delete.setImageResource(R.drawable.ic_undelete); + } else { + holder.delete.setImageResource(R.drawable.ic_delete); + } + } + + @Override + public int getItemCount() { + return mList.size(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + + @BindView(R.id.title) + TextView title; + + @BindView(R.id.version_name) + TextView versionName; + @BindView(R.id.description) + TextView description; + @BindView(R.id.warning) + TextView warning; + @BindView(R.id.checkbox) + CheckBox checkBox; + @BindView(R.id.author) + TextView author; + @BindView(R.id.updateStatus) + TextView updateStatus; + @BindView(R.id.delete) + ImageView delete; + @BindView(R.id.changeLog) + ImageView changeLog; + @BindView(R.id.authorLink) + ImageView authorLink; + @BindView(R.id.supportLink) + ImageView supportLink; + @BindView(R.id.expand_layout) + LinearLayout expandLayout; + private ValueAnimator mAnimator; + private int mMeasuredHeight; + + public ViewHolder(View itemView) { + super(itemView); + WindowManager windowmanager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + ButterKnife.bind(this, itemView); + DisplayMetrics dimension = new DisplayMetrics(); + windowmanager.getDefaultDisplay().getMetrics(dimension); + + expandLayout.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + + + @Override + public boolean onPreDraw() { + expandLayout.getViewTreeObserver().removeOnPreDrawListener(this); + expandLayout.setVisibility(View.GONE); + final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + expandLayout.measure(widthSpec, heightSpec); + mAnimator = slideAnimator(0, expandLayout.getMeasuredHeight()); + return true; + } + }); + + viewMain.setOnClickListener(view -> { + int position = getAdapterPosition(); + Log.d("Magisk", "ReposFragment: CLICK. " + position + " and " + mExpandedList.get(position)); + + if (mExpandedList.get(position)) { + collapse(expandLayout); + } else { + expand(expandLayout); + } + mExpandedList.set(position, !mExpandedList.get(position)); + + }); + if (!Shell.rootAccess()) { + checkBox.setEnabled(false); + delete.setEnabled(false); + } + } + + private void expand(View view) { + + // set Visible + + + Log.d("Magisk", "ReposFragment: Expand anim called " + mMeasuredHeight + " and " + view.getId()); + view.setVisibility(View.VISIBLE); + mAnimator.start(); + } + + private void collapse(View view) { + int finalHeight = view.getHeight(); + ValueAnimator mAnimator = slideAnimator(finalHeight, 0); + Log.d("Magisk", "ReposFragment: Collapse anim called " + finalHeight + " and " + view.getId()); + + mAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationEnd(Animator animator) { + // Height=0, but it set visibility to GONE + view.setVisibility(View.GONE); + } + + @Override + public void onAnimationStart(Animator animator) { + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }); + mAnimator.start(); + } + + private ValueAnimator slideAnimator(int start, int end) { + + ValueAnimator animator = ValueAnimator.ofInt(start, end); + + animator.addUpdateListener(valueAnimator -> { + // Update Height + int value = (Integer) valueAnimator.getAnimatedValue(); + + ViewGroup.LayoutParams layoutParams = expandLayout + .getLayoutParams(); + layoutParams.height = value; + expandLayout.setLayoutParams(layoutParams); + }); + return animator; + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java index ecaa9f18c..4c7cee645 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java @@ -1,149 +1,66 @@ package com.topjohnwu.magisk; -import android.content.Intent; -import android.net.Uri; +import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.TabLayout; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.Log; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ProgressBar; -import android.widget.Toast; +import android.widget.CheckBox; +import android.widget.TextView; -import com.ipaulpro.afilechooser.FileInfo; -import com.ipaulpro.afilechooser.utils.FileUtils; import com.topjohnwu.magisk.module.Module; -import com.topjohnwu.magisk.module.RepoHelper; import com.topjohnwu.magisk.utils.Utils; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; public class ModulesFragment extends Fragment { + @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; + @BindView(R.id.recyclerView) RecyclerView recyclerView; + @BindView(R.id.empty_rv) TextView emptyTv; - private static final int FETCH_ZIP_CODE = 2; + private SharedPreferences prefs; public static List listModules = new ArrayList<>(); - public static List listModulesCache = new ArrayList<>(); - @BindView(R.id.progressBar) - ProgressBar progressBar; - @BindView(R.id.fab) - FloatingActionButton fabio; - @BindView(R.id.pager) - ViewPager viewPager; - @BindView(R.id.tab_layout) - TabLayout tabLayout; - private int viewPagePosition; - private RepoHelper.TaskDelegate mTaskDelegate; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.modules_fragment, container, false); + View viewMain = inflater.inflate(R.layout.modules_fragment, container, false); - ButterKnife.bind(this, view); - fabio.setOnClickListener(v -> { - Intent getContentIntent = FileUtils.createGetContentIntent(null); - getContentIntent.setType("application/zip"); - Intent fileIntent = Intent.createChooser(getContentIntent, "Select a file"); - startActivityForResult(fileIntent, FETCH_ZIP_CODE); + ButterKnife.bind(this, viewMain); + + prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + mSwipeRefreshLayout.setOnRefreshListener(() -> { + + recyclerView.setVisibility(View.GONE); + new Utils.LoadModules(getActivity()).execute(); + new updateUI().execute(); + prefs.edit().putBoolean("ignoreUpdateAlerts", false).apply(); }); - new Utils.LoadModules(getActivity()).execute(); - mTaskDelegate = result -> { - if (result.equals("OK")) { - RefreshUI(); - } + prefs.registerOnSharedPreferenceChangeListener((sharedPreferences, s) -> { + if (s.contains("updated")) { + viewMain.invalidate(); + viewMain.requestLayout(); - }; + } + }); new updateUI().execute(); - return view; - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (data != null) { - // Get the URI of the selected file - final Uri uri = data.getData(); - Log.i("Magisk", "ModulesFragment: Uri = " + uri.toString() + " or "); - new Utils.FlashZIP(getActivity(), uri).execute(); - try { - // Get the file path from the URI - FileInfo fileInfo = FileUtils.getFileInfo(getActivity(), uri); - Toast.makeText(getActivity(), - "File Selected: " + fileInfo.getDisplayName() + " size: " + fileInfo.getSize(), Toast.LENGTH_LONG).show(); - - if (!fileInfo.isExternal()) { - - } else { - - } - } catch (Exception e) { - Log.e("FileSelectorTestAc...", "File select error", e); - } - } - - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_module, menu); - - } - - private void RefreshUI() { - viewPagePosition = tabLayout.getSelectedTabPosition(); - listModules.clear(); - listModulesCache.clear(); - progressBar.setVisibility(View.VISIBLE); - viewPager.setAdapter(new TabsAdapter(getChildFragmentManager())); - tabLayout.setupWithViewPager(viewPager); - viewPager.setCurrentItem(viewPagePosition); - new Utils.LoadModules(getActivity()).execute(); - Collections.sort(listModules, new CustomComparator()); - Collections.sort(listModulesCache, new CustomComparator()); - new updateUI().execute(); - } - - void selectPage(int pageIndex) { - tabLayout.setScrollPosition(pageIndex, 0f, true); - viewPager.setCurrentItem(pageIndex); - } - - public static class NormalModuleFragment extends BaseModuleFragment { - - @Override - protected List listModules() { - return listModules; - } - - } - - public static class CacheModuleFragment extends BaseModuleFragment { - - @Override - protected List listModules() { - return listModulesCache; - } + return viewMain; } private class updateUI extends AsyncTask { @@ -156,53 +73,38 @@ public class ModulesFragment extends Fragment { @Override protected void onPostExecute(Void v) { super.onPostExecute(v); - progressBar.setVisibility(View.GONE); - viewPager.setAdapter(new TabsAdapter(getChildFragmentManager())); - tabLayout.setupWithViewPager(viewPager); - selectPage(viewPagePosition); - } - } - - private class TabsAdapter extends FragmentPagerAdapter { - - String[] tabTitles = new String[]{ - getString(R.string.modules), getString(R.string.cache_modules) - }; - - public TabsAdapter(FragmentManager fm) { - super(fm); - } - - @Override - public int getCount() { - return tabTitles.length; - } - - @Override - public String getPageTitle(int position) { - return tabTitles[position]; - } - - @Override - public Fragment getItem(int position) { - if (position == 0) { - NormalModuleFragment nmf = new NormalModuleFragment(); - nmf.SetDelegate(mTaskDelegate); - return nmf; + if (listModules.size() == 0) { + emptyTv.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); } else { - CacheModuleFragment cmf = new CacheModuleFragment(); - cmf.SetDelegate(mTaskDelegate); - return cmf; + recyclerView.setVisibility(View.VISIBLE); } + recyclerView.setAdapter(new ModulesAdapter(listModules, (chk, position) -> { + // On Checkbox change listener + CheckBox chbox = (CheckBox) chk; + + if (!chbox.isChecked()) { + listModules.get(position).createDisableFile(); + Snackbar.make(chk, R.string.disable_file_created, Snackbar.LENGTH_SHORT).show(); + } else { + listModules.get(position).removeDisableFile(); + Snackbar.make(chk, R.string.disable_file_removed, Snackbar.LENGTH_SHORT).show(); + } + }, (deleteBtn, position) -> { + // On delete button click listener + listModules.get(position).createRemoveFile(); + Snackbar.make(deleteBtn, R.string.remove_file_created, Snackbar.LENGTH_SHORT).show(); + }, (undeleteBtn, position) -> { + // On undelete button click listener + listModules.get(position).deleteRemoveFile(); + Snackbar.make(undeleteBtn, R.string.remove_file_deleted, Snackbar.LENGTH_SHORT).show(); + })); + + if (mSwipeRefreshLayout.isRefreshing()) + mSwipeRefreshLayout.setRefreshing(false); + } } - public class CustomComparator implements Comparator { - @Override - public int compare(Module o1, Module o2) { - return o1.getName().compareTo(o2.getName()); - } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/MonitorService.java b/app/src/main/java/com/topjohnwu/magisk/MonitorService.java index c06db43cf..736c35784 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MonitorService.java +++ b/app/src/main/java/com/topjohnwu/magisk/MonitorService.java @@ -8,6 +8,8 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -34,6 +36,7 @@ public class MonitorService extends Service private Boolean disableroot; private Boolean disablerootprev; private int counter = 0; + private String mPackageName; @Nullable @Override @@ -49,6 +52,18 @@ public class MonitorService extends Service } + @Override + public void onCreate() { + super.onCreate(); + Log.d("Magisk","MonitorService: Service created"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d("Magisk","MonitorService: Service destroyed"); + } + private Runnable checkProcesses = new Runnable() { @Override public void run() { @@ -86,51 +101,20 @@ public class MonitorService extends Service private void ForceDisableRoot() { Log.d("Magisk", "MonitorService: Forcedisable called."); - Shell.su("setprop magisk.root 0"); - - if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (Utils.rootStatus()) { - Shell.su(("setprop magisk.root 0")); + Utils.toggleRoot(false); + if (Utils.rootEnabled()) { + Utils.toggleRoot(false); Log.d(TAG, "MonitorService: FORCING."); } - Log.d("Magisk", "MonitorService: Forcedisable called. " + Utils.rootStatus()); + Log.d("Magisk", "MonitorService: Forcedisable called. " + Utils.rootEnabled()); } private void ForceEnableRoot() { Log.d("Magisk", "MonitorService: ForceEnable called."); - if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } else if (!Utils.rootStatus()) { - Shell.su(("setprop magisk.root 1")); - Log.d(TAG, "MonitorService: FORCING."); - } + Utils.toggleRoot(true); + if (!Utils.rootEnabled()) { + Utils.toggleRoot(true); + } } private void ShowNotification(boolean rootAction) { @@ -141,18 +125,23 @@ public class MonitorService extends Service Intent intent = new Intent(getApplication(), WelcomeActivity.class); intent.putExtra("relaunch", "relaunch"); + String rootMessage; PendingIntent pendingIntent = PendingIntent.getActivity( getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - + if (mPackageName.equals("")) { + rootMessage = "Root has been disabled"; + } else { + rootMessage = "Root has been disabled for " + mPackageName; + } mBuilder = new NotificationCompat.Builder(getApplicationContext()) .setSmallIcon(disableroot ? R.drawable.ic_stat_notification_autoroot_off : R.drawable.ic_stat_notification_autoroot_on) .setContentIntent(pendingIntent) .setContentTitle("Auto-root status changed") - .setContentText("Root has been disabled."); + .setContentText(rootMessage); int mNotificationId = 1; mNotifyMgr.notify(mNotificationId, mBuilder.build()); } else { @@ -178,6 +167,20 @@ public class MonitorService extends Service return false; } + private String getAppName (String packageName) { + PackageManager pkManager = getPackageManager(); + ApplicationInfo appInfo; + String appname = ""; + try { + appInfo = pkManager.getApplicationInfo(packageName, 0); + appname = (String) ((appInfo != null) ? pkManager.getApplicationLabel(appInfo) : "???"); + return appname; + } catch (final PackageManager.NameNotFoundException e) { + return null; + } + } + + protected boolean isAppForeground(String packageName) { UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); long time = System.currentTimeMillis(); @@ -195,8 +198,9 @@ public class MonitorService extends Service if (topPackageName.equals("com.topjohnwu.magisk")) { mySortedMap.remove(mySortedMap.lastKey()); topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName(); + mPackageName = getAppName(topPackageName); } - //Log.d("Magisk", "MonitorService: Hi Captain, the package we need to kill for is " + topPackageName); + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java index c4c6b8a64..0342c2dc4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java @@ -1,9 +1,7 @@ package com.topjohnwu.magisk; -import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; -import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; @@ -50,7 +48,7 @@ public class ReposFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.single_repo_fragment, container, false); + View view = inflater.inflate(R.layout.repos_fragment, container, false); mView = view; ButterKnife.bind(this, view); prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); @@ -114,8 +112,7 @@ public class ReposFragment extends Fragment { }; Log.d("Magisk","ReposFragment, LoadRepo called"); mListRepos.clear(); - RepoHelper mr = new RepoHelper(); - List magiskRepos = mr.listRepos(getActivity(), doReload, taskDelegate); + List magiskRepos = RepoHelper.listRepos(getActivity(), doReload, taskDelegate); for (Repo repo : magiskRepos) { Log.d("Magisk", "ReposFragment: Adding repo from string " + repo.getId()); diff --git a/app/src/main/java/com/topjohnwu/magisk/RootFragment.java b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java index 6206c0d98..86ee91f29 100644 --- a/app/src/main/java/com/topjohnwu/magisk/RootFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java @@ -1,5 +1,6 @@ package com.topjohnwu.magisk; +import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; @@ -8,8 +9,10 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; +import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -18,6 +21,7 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Switch; import android.widget.TextView; +import android.widget.Toast; import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Utils; @@ -90,6 +94,8 @@ public class RootFragment extends Fragment { ButterKnife.bind(this, view); prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + if (prefs.contains("autoRootEnable")) { autoRootStatus = prefs.getBoolean("autoRootEnable",false); rootToggle.setEnabled(false); @@ -119,22 +125,51 @@ public class RootFragment extends Fragment { return view; } - private void ToggleAutoRoot(boolean toggleState) { - autoRootStatus = toggleState; - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean("autoRootEnable", (toggleState)); - editor.apply(); - if (toggleState) { - Intent myIntent = new Intent(getActivity(), MonitorService.class); - getActivity().startService(myIntent); - rootToggle.setEnabled(false); - boolean boo = Utils.isMyServiceRunning(MonitorService.class, getActivity()); - if (boo) { - Intent myServiceIntent = new Intent(getActivity(), MonitorService.class); - getActivity().startService(myServiceIntent); + + private void CheckAccessPermissions() { + if (!Utils.hasStatsPermission(getActivity())) { + Toast.makeText(getActivity(),"Please allow Usage Access for auto root to work.",Toast.LENGTH_LONG).show(); + startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), 100); + } + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // Check which request we're responding to + if (requestCode == 100) { + // Make sure the request was successful + if (resultCode == Activity.RESULT_OK) { + Log.d("Magisk","Got result code OK for permissions"); + Toast.makeText(getActivity(),"Auto-root disabled, permissions required.",Toast.LENGTH_LONG).show(); + + + } else { + autoRootToggle.setEnabled(false); + } + + } + } + + private void ToggleAutoRoot(boolean toggleState) { + CheckAccessPermissions(); + if (Utils.hasStatsPermission(getActivity())) { + autoRootStatus = toggleState; + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("autoRootEnable", (toggleState)); + editor.apply(); + if (toggleState) { + Intent myIntent = new Intent(getActivity(), MonitorService.class); + getActivity().startService(myIntent); + rootToggle.setEnabled(false); + boolean boo = Utils.isMyServiceRunning(MonitorService.class, getActivity()); + if (boo) { + Intent myServiceIntent = new Intent(getActivity(), MonitorService.class); + getActivity().startService(myServiceIntent); + } + } else { + rootToggle.setEnabled(true); } - } else { - rootToggle.setEnabled(true); } } @@ -233,25 +268,25 @@ public class RootFragment extends Fragment { break; } else { rootToggle.setEnabled(true); - if (new File("/magisk/.core/bin/su").exists()) { + if (Utils.rootEnabled()) { // Mounted rootStatusContainer.setBackgroundColor(accent); rootStatusIcon.setImageResource(statusError); rootStatus.setTextColor(accent); - rootStatus.setText(R.string.root_mounted); + rootStatus.setText(R.string.root_enabled); rootToggle.setChecked(true); safetyNetStatusIcon.setImageResource(statusError); - safetyNetStatus.setText(R.string.root_mounted_info); + safetyNetStatus.setText(R.string.root_enabled_info); break; } else { - // Not Mounted + // Disabled rootStatusContainer.setBackgroundColor(green500); rootStatusIcon.setImageResource(statusOK); rootStatus.setTextColor(green500); - rootStatus.setText(R.string.root_unmounted); + rootStatus.setText(R.string.root_disabled); rootToggle.setChecked(false); safetyNetStatusIcon.setImageResource(statusOK); - safetyNetStatus.setText(R.string.root_unmounted_info); + safetyNetStatus.setText(R.string.root_disabled_info); break; } } diff --git a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java index 7116f727b..bbf2bf2e0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java @@ -1,11 +1,10 @@ package com.topjohnwu.magisk; import android.Manifest; -import android.app.AppOpsManager; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -53,10 +52,6 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView setContentView(R.layout.activity_welcome); ButterKnife.bind(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } - // Startups PreferenceManager.setDefaultValues(this, R.xml.defaultpref, false); if (!Utils.isMyServiceRunning(MonitorService.class, getApplicationContext())) { @@ -67,9 +62,7 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } - if (!hasPermission()) { - startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), 100); - } + new Utils.Initialize(this).execute(); new Utils.CheckUpdates(this).execute(); @@ -77,16 +70,12 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView RepoHelper.TaskDelegate delegate = result -> { //Do a thing here when we get a result we want }; - if (!prefs.contains("hasCachedRepos")) { - new Utils.LoadModules(this, true).execute(); + new Utils.LoadModules(this).execute(); new Utils.LoadRepos(this, true, delegate).execute(); - - } else { - new Utils.LoadModules(getApplication(), false).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(); - } - setSupportActionBar(toolbar); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) { @@ -146,14 +135,7 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView return true; } - private boolean hasPermission() { - AppOpsManager appOps = (AppOpsManager) - getSystemService(Context.APP_OPS_SERVICE); - int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, - android.os.Process.myUid(), getPackageName()); - return mode == AppOpsManager.MODE_ALLOWED; - } private void navigate(final int itemId) { Fragment navFragment = null; diff --git a/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java b/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java new file mode 100644 index 000000000..707ee371e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/module/BaseModule.java @@ -0,0 +1,103 @@ +package com.topjohnwu.magisk.module; + + +import android.util.Log; + +import java.util.List; + +public abstract class BaseModule { + + protected String mId, mName, mVersion, mDescription, mSupportUrl, mDonateUrl, mAuthor; + protected boolean mIsCacheModule = false; + + protected int mVersionCode = 0; + + public BaseModule(List props) { + this(props.toArray(new String[props.size()])); + } + + public BaseModule(String[] props) { + for (String line : props) { + String[] prop = line.split("=", 2); + if (prop.length != 2) { + continue; + } + + String key = prop[0].trim(); + if (key.charAt(0) == '#') { + continue; + } + + switch (key) { + case "id": + this.mId = prop[1]; + break; + case "name": + this.mName = prop[1]; + break; + case "version": + this.mVersion = prop[1]; + break; + case "versionCode": + this.mVersionCode = Integer.parseInt(prop[1]); + break; + case "author": + this.mAuthor = prop[1]; + break; + case "description": + this.mDescription = prop[1]; + break; + case "support": + this.mSupportUrl = prop[1]; + break; + case "donate": + this.mDonateUrl = prop[1]; + break; + case "cacheModule": + this.mIsCacheModule = Boolean.parseBoolean(prop[1]); + break; + default: + Log.d("Magisk", "Module: Manifest string not recognized: " + prop[0]); + break; + } + } + } + + public String getName() { + return mName; + } + + public String getVersion() { + return mVersion; + } + + public String getAuthor() { + return mAuthor; + } + + public String getId() {return mId; } + + public String getDescription() { + return mDescription; + } + + public boolean isCache() { + return mIsCacheModule; + } + + public void setCache() { + mIsCacheModule = true; + } + + public int getVersionCode() { + return mVersionCode; + } + + public String getmDonateUrl() { + return mDonateUrl; + } + + public String getmSupportUrl() { + 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 f052971be..2e3dc3d35 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Module.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Module.java @@ -5,89 +5,39 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; +import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.Utils; -public class Module { +public class Module extends BaseModule { private String mRemoveFile; private String mDisableFile; - private String mName = null; - private String mVersion = "(No version provided)"; - private String mDescription = "(No description provided)"; - private String mUrl,mSupportUrl,mDonateUrl,mZipUrl,mBaseUrl,mManifestUrl,mAuthor,mLogUrl; - private boolean mEnable, mRemove,mUpdateAvailable, mIsInstalled,mIsCacheModule; - - - private String mId; - private int mVersionCode; + private String mZipUrl, mLogUrl; + private boolean mEnable, mRemove, mUpdateAvailable = false, mIsInstalled; public Module(String path, Context context) { + super(Utils.readFile(path + "/module.prop")); + mRemoveFile = path + "/remove"; mDisableFile = path + "/disable"; - for (String line : Utils.readFile(path + "/module.prop")) { - String[] props = line.split("=", 2); - if (props.length != 2) { - continue; - } - - String key = props[0].trim(); - if (key.charAt(0) == '#') { - continue; - } - - switch (props[0]) { - case "versionCode": - this.mVersionCode = Integer.valueOf(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 "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; - case "logUrl": - this.mLogUrl = props[1]; - break; - default: - Log.d("Magisk", "Module: Manifest string not recognized: " + props[0]); - break; - } - + if (mId == null) { + int sep = path.lastIndexOf('/'); + mId = path.substring(sep + 1); } - Log.d("Magisk","Module: Loaded module with ID of " + this.mId + " or " + mId); + + if (mName == null) + mName = mId; + + if (mDescription == null) + mDescription = context.getString(R.string.no_info_provided); + + if (mVersion == null) + mVersion = context.getString(R.string.no_info_provided); + + Log.d("Magisk","Module: Loaded module with ID of " + mId ); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); @@ -154,52 +104,13 @@ public class Module { editor.apply(); } - if (mName == null) { - int sep = path.lastIndexOf('/'); - mName = path.substring(sep + 1); - mId = mName; - } - mEnable = !Utils.fileExist(mDisableFile); mRemove = Utils.fileExist(mRemoveFile); } - public Module(Repo repo) { - - mName = repo.getName(); - mVersion = repo.getmVersion(); - mDescription = repo.getDescription(); - mId = repo.getId(); - mVersionCode = repo.getmVersionCode(); - mUrl = repo.getmZipUrl(); - mEnable = true; - mRemove = false; - - } - - - - public String getName() { - return mName; - } - - public String getVersion() { - return mVersion; - } - - public String getAuthor() { - return mAuthor; - } - - public String getId() {return mId; } - public String getmLogUrl() {return mLogUrl; } - public String getDescription() { - return mDescription; - } - public void createDisableFile() { mEnable = !Utils.createFile(mDisableFile); } @@ -224,22 +135,8 @@ public class Module { return mRemove; } - public String getmDonateUrl() { - return mDonateUrl; - } - public String getmZipUrl() { return mZipUrl; } - public String getmManifestUrl() { - return mManifestUrl; - } - - public String getmSupportUrl() { - return mSupportUrl; - } - - public boolean isInstalled() {return mIsInstalled; } - public boolean isUpdateAvailable() { return mUpdateAvailable; } } \ No newline at end of file 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 23c74dcdf..16461758e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Repo.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Repo.java @@ -64,9 +64,8 @@ public class Repo { public void fetch() { - WebRequest webreq = new WebRequest(); // Construct initial url for contents - String repoString = webreq.makeWebServiceCall(mBaseUrl + "/contents?access_token=" + Utils.procFile(appContext.getString(R.string.some_string),appContext), WebRequest.GET); + 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++) { @@ -89,8 +88,7 @@ public class Repo { e.printStackTrace(); } - WebRequest propReq = new WebRequest(); - String manifestString = propReq.makeWebServiceCall(mManifestUrl,WebRequest.GET,true); + String manifestString = WebRequest.makeWebServiceCall(mManifestUrl, WebRequest.GET, true); if (ParseProps(manifestString)) { PutProps(manifestString); 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 664604045..304e179d3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/RepoHelper.java @@ -29,35 +29,32 @@ import java.util.Map; public class RepoHelper { private static List repos = new ArrayList<>(); private static String TAG = "Magisk"; - private Context activityContext; - private Date updatedDate; - private SharedPreferences prefs; - private boolean apiFail; public RepoHelper() { } - public List listRepos(Context context, boolean refresh, TaskDelegate delegate) { - prefs = PreferenceManager.getDefaultSharedPreferences(context); - activityContext = context; + 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).execute(); + 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(); + BuildFromCache(context); } Collections.sort(repos, new CustomComparator()); return repos; } - private void BuildFromCache() { + 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_")) { @@ -67,12 +64,16 @@ public class RepoHelper { } } - class BuildFromWeb extends AsyncTask { + static class BuildFromWeb extends AsyncTask { private TaskDelegate delegate; + private SharedPreferences prefs; + private Context activityContext; - public BuildFromWeb(TaskDelegate delegate) { + public BuildFromWeb(TaskDelegate delegate, Context activityContext) { this.delegate = delegate; + this.activityContext = activityContext; + prefs = PreferenceManager.getDefaultSharedPreferences(activityContext); } @Override @@ -90,15 +91,13 @@ public class RepoHelper { } @Override - protected Void doInBackground(String... params) { + protected Boolean doInBackground(String... params) { publishProgress(); - // Creating service handler class instance - WebRequest webreq = new WebRequest(); // 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 = webreq.makeWebServiceCall(url1 + Utils.procFile(token, activityContext), WebRequest.GET); + String jsonStr = WebRequest.makeWebServiceCall(url1 + Utils.procFile(token, activityContext), WebRequest.GET); if (jsonStr != null && !jsonStr.isEmpty()) { try { @@ -115,6 +114,7 @@ public class RepoHelper { 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)) { @@ -147,6 +147,7 @@ public class RepoHelper { } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); + return true; } if (!name.contains("Repo.github.io")) { if (doUpdate) { @@ -164,21 +165,19 @@ public class RepoHelper { } catch (JSONException e) { e.printStackTrace(); } - apiFail = false; + return false; } else { - apiFail = true; + return true; } - return null; - } - protected void onPostExecute(Void v) { + 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(); + BuildFromCache(activityContext); } @@ -189,7 +188,7 @@ public class RepoHelper { void taskCompletionResult(String result); } - public class CustomComparator implements Comparator { + 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/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 7f55aca79..8987096b9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -3,6 +3,7 @@ package com.topjohnwu.magisk.utils; import android.Manifest; import android.app.Activity; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.DownloadManager; import android.app.ProgressDialog; import android.content.BroadcastReceiver; @@ -170,6 +171,15 @@ public class Utils { return value; } + public static boolean hasStatsPermission(Context context) { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, + android.os.Process.myUid(), context.getPackageName()); + return mode == AppOpsManager.MODE_ALLOWED; + + } + + public abstract static class DownloadReceiver extends BroadcastReceiver { public Context mContext; long downloadID; @@ -438,7 +448,7 @@ public class Utils { try { String rootStatus = Shell.su("getprop magisk.root").toString(); String fuckyeah = Shell.sh("which su").toString(); - Log.d("Magisk","Utils: Rootstatus Checked, " + rootStatus + " and " + fuckyeah); + Log.d("Magisk", "Utils: Rootstatus Checked, " + rootStatus + " and " + fuckyeah); if (rootStatus.contains("0") && !fuckyeah.contains("su")) { return false; } else { @@ -448,7 +458,6 @@ public class Utils { e.printStackTrace(); return false; } - } public static boolean isMyServiceRunning(Class serviceClass, Context context) { @@ -457,7 +466,7 @@ public class Utils { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } - } + } return false; } @@ -472,7 +481,6 @@ public class Utils { @Override protected Void doInBackground(Void... voids) { ModulesFragment.listModules.clear(); - ModulesFragment.listModulesCache.clear(); List magisk = getModList(MAGISK_PATH); Log.d("Magisk", "Utils: Reload called, loading modules"); List magiskCache = getModList(MAGISK_CACHE_PATH); @@ -484,7 +492,10 @@ public class Utils { for (String mod : magiskCache) { Log.d("Magisk", "Utils: Adding cache module from string " + mod); - ModulesFragment.listModulesCache.add(new Module(mod, mContext)); + Module cacheMod = new Module(mod, mContext); + // Prevent people forgot to change module.prop + cacheMod.setCache(); + ModulesFragment.listModules.add(cacheMod); } return null; @@ -507,8 +518,7 @@ public class Utils { @Override protected Void doInBackground(Void... voids) { ReposFragment.mListRepos.clear(); - RepoHelper mr = new RepoHelper(); - List magiskRepos = mr.listRepos(mContext, doReload, mTaskDelegate); + List magiskRepos = RepoHelper.listRepos(mContext, doReload, mTaskDelegate); for (Repo repo : magiskRepos) { Log.d("Magisk", "Utils: Adding repo from string " + repo.getId()); @@ -545,13 +555,13 @@ public class Utils { final String docId = DocumentsContract.getDocumentId(mUri); Log.d("Magisk", "Utils: FlashZip Running, " + docId + " and " + mUri.toString()); - String[] split = docId.split(":"); - mName = split[1]; - if (mName.contains("/")) { - split = mName.split("/"); - } - if (split[1].contains(".zip")) { - file = mContext.getFilesDir() + "/" + split[1]; + 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()); @@ -615,6 +625,7 @@ public class Utils { 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; } @@ -622,11 +633,11 @@ public class Utils { return false; } else { ret = Shell.su( - "rm -rf /data/tmp", - "mkdir -p /data/tmp", - "cp -af " + mPath + " /data/tmp/install.zip", - "unzip -o /data/tmp/install.zip META-INF/com/google/android/* -d /data/tmp", - "BOOTMODE=true sh /data/tmp/META-INF/com/google/android/update-binary dummy 1 /data/tmp/install.zip", + "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)); @@ -636,7 +647,6 @@ public class Utils { @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); - Shell.su("rm -rf /data/tmp"); if (deleteFileAfter) { Shell.su("rm -rf " + mPath); Log.d("Magisk", "Utils: Deleting file " + mPath); diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/WebRequest.java b/app/src/main/java/com/topjohnwu/magisk/utils/WebRequest.java index d01649e97..1e6d2a535 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/WebRequest.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/WebRequest.java @@ -20,7 +20,6 @@ public class WebRequest { static String response = null; public final static int GET = 1; public final static int POST = 2; - private boolean addNewLine; //Constructor with no parameter public WebRequest() { @@ -33,18 +32,16 @@ public class WebRequest { * @url - url to make request * @requestmethod - http request method */ - public String makeWebServiceCall(String url, int requestmethod) { - addNewLine=false; + public static String makeWebServiceCall(String url, int requestmethod) { Log.d("Magisk","WebRequest: Service call received for URL " + url); - return this.makeWebServiceCall(url, requestmethod, null); + return makeWebServiceCall(url, requestmethod, null, false); } - public String makeWebServiceCall(String url, int requestmethod, boolean addNewLines) { - addNewLine = addNewLines; + public static String makeWebServiceCall(String url, int requestmethod, boolean addNewLines) { Log.d("Magisk","WebRequest: Service call(bool) received for URL " + url); - return this.makeWebServiceCall(url, requestmethod, null); + return makeWebServiceCall(url, requestmethod, null, addNewLines); } @@ -55,8 +52,8 @@ public class WebRequest { * @requestmethod - http request method * @params - http request params */ - public String makeWebServiceCall(String urladdress, int requestmethod, - HashMap params) { + public static String makeWebServiceCall(String urladdress, int requestmethod, + HashMap params, boolean addNewLines) { URL url; String response = ""; try { @@ -104,7 +101,7 @@ public class WebRequest { String line; BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); while ((line = br.readLine()) != null) { - if (addNewLine) { + if (addNewLines) { response += line + "\n"; } else { response += line; diff --git a/app/src/main/res/layout/app_list_row.xml b/app/src/main/res/layout/app_list_row.xml index b69170f44..0b7a2d156 100644 --- a/app/src/main/res/layout/app_list_row.xml +++ b/app/src/main/res/layout/app_list_row.xml @@ -1,56 +1,59 @@ + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:padding="5dp"> + android:scaleType="centerCrop"/> + - - - + android:orientation="vertical" + android:paddingLeft="5dp" + android:paddingRight="50dp"> - + - + + + - + + + android:scaleType="centerCrop"/> - + \ No newline at end of file diff --git a/app/src/main/res/layout/auto_root_fragment.xml b/app/src/main/res/layout/auto_root_fragment.xml index 07aed4038..214589074 100644 --- a/app/src/main/res/layout/auto_root_fragment.xml +++ b/app/src/main/res/layout/auto_root_fragment.xml @@ -4,6 +4,8 @@ android:orientation="vertical" > diff --git a/app/src/main/res/layout/modules_fragment.xml b/app/src/main/res/layout/modules_fragment.xml index e23327a92..336fbdb93 100644 --- a/app/src/main/res/layout/modules_fragment.xml +++ b/app/src/main/res/layout/modules_fragment.xml @@ -1,67 +1,31 @@ - + android:layout_height="fill_parent" + android:layout_marginTop="?attr/actionBarSize" + android:orientation="vertical"> - + - + - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/single_repo_fragment.xml b/app/src/main/res/layout/repos_fragment.xml similarity index 100% rename from app/src/main/res/layout/single_repo_fragment.xml rename to app/src/main/res/layout/repos_fragment.xml diff --git a/app/src/main/res/layout/single_module_fragment.xml b/app/src/main/res/layout/single_module_fragment.xml deleted file mode 100644 index 336fbdb93..000000000 --- a/app/src/main/res/layout/single_module_fragment.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9d8d18d86..74b9fea33 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,13 +28,12 @@ Safety Net Status Unknown Not Rooted Safety Net (Android Pay) should work - Root mounted - Root mounted and enabled. Safety Net (Android Pay) will NOT work. - Root not mounted - Safety Net (Android Pay) should work, but no root temporarily\nYou might need to manually add a card in Android Pay app to refresh the root status of AP + Root enabled + Root enabled. Safety Net (Android Pay) will NOT work + Root disabled + Safety Net (Android Pay) should work, but no root temporarily\nYou might need to manually add a card in Android Pay app to refresh the root status of AP Root set to auto-mount Root will auto unmount for selected applications. Safety Net (Android Pay) should work. - Magisk Incompatible Root Detected Root improperly installed. Safety Net (Android Pay) will NOT work, and impossible to toggle. @@ -44,6 +43,7 @@ Samsung need custom kernel for switching SELinux status! + (No info provided) Cache modules No modules found An update is available! @@ -98,6 +98,7 @@ phh\'s superuser SuperSU It seems that you have incompatible root installed\nDo you want to install Magisk compatible root now? + MagiskRox666 GTYybRBTYf5his9kQ16ZNO7qgkBJ/5MyVe4CGceAOIoXgSnnk8FTd4F1dE9p5Eus Downloads