diff --git a/app/build.gradle b/app/build.gradle index be33cb63a..054ffa779 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ android { applicationId "com.topjohnwu.magisk" minSdkVersion 21 targetSdkVersion 24 - versionCode 3 + versionCode 4 versionName "2.0" } buildTypes { diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java index 13dde5232..57ba0a210 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java @@ -1,21 +1,47 @@ package com.topjohnwu.magisk; +import android.Manifest; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; +import android.support.v7.app.AlertDialog; +import android.text.Html; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.CompoundButton; 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 org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.File; +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.net.URLConnection; import java.util.List; import butterknife.BindColor; @@ -24,67 +50,105 @@ import butterknife.ButterKnife; public class MagiskFragment extends Fragment { - @BindView(R.id.progressBar) ProgressBar progressBar; + private static final String JSON_UPDATE_CHECK = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/master/app/magisk_update.json"; + + @BindView(R.id.progressBarVersion) ProgressBar progressBar; - @BindView(R.id.rootSwitchView) View rootToggleView; - @BindView(R.id.selinuxSwitchView) View selinuxToggleView; @BindView(R.id.magiskStatusView) View magiskStatusView; - @BindView(R.id.rootStatusView) View rootStatusView; - @BindView(R.id.safetynetStatusView) View safetynetStatusView; - @BindView(R.id.selinuxStatusView) View selinuxStatusView; - - @BindView(R.id.root_toggle) Switch rootToggle; - @BindView(R.id.selinux_toggle) Switch selinuxToggle; - @BindView(R.id.magisk_status_container) View magiskStatusContainer; @BindView(R.id.magisk_status_icon) ImageView magiskStatusIcon; @BindView(R.id.magisk_version) TextView magiskVersion; - @BindView(R.id.root_status_container) View rootStatusContainer; - @BindView(R.id.root_status_icon) ImageView rootStatusIcon; - @BindView(R.id.root_status) TextView rootStatus; + @BindView(R.id.app_updateView) View appUpdateView; + @BindView(R.id.app_check_updates_container) View appCheckUpdatesContainer; + @BindView(R.id.app_check_updates_icon) ImageView appCheckUpdatesIcon; + @BindView(R.id.app_check_updates_status) TextView appCheckUpdatesStatus; + @BindView(R.id.app_check_updates_progress) ProgressBar appCheckUpdatesProgress; - @BindView(R.id.selinux_status_container) View selinuxStatusContainer; - @BindView(R.id.selinux_status_icon) ImageView selinuxStatusIcon; - @BindView(R.id.selinux_status) TextView selinuxStatus; + @BindView(R.id.magisk_updateView) View magiskUpdateView; + @BindView(R.id.magisk_check_updates_container) View magiskCheckUpdatesContainer; + @BindView(R.id.magisk_check_updates_icon) ImageView magiskCheckUpdatesIcon; + @BindView(R.id.magisk_check_updates_status) TextView magiskCheckUpdatesStatus; + @BindView(R.id.magisk_check_updates_progress) ProgressBar magiskCheckUpdatesProgress; - @BindView(R.id.safety_net_status) TextView safetyNetStatus; - @BindView(R.id.safety_net_icon) ImageView safetyNetStatusIcon; - - @BindColor(R.color.red500) int red500; @BindColor(R.color.green500) int green500; - @BindColor(R.color.grey500) int grey500; @BindColor(R.color.accent) int accent; + @BindColor(R.color.blue500) int blue500; + @BindColor(R.color.grey500) int grey500; int statusOK = R.drawable.ic_check_circle; - int statusError = R.drawable.ic_error; int statusUnknown = R.drawable.ic_help; + private String mLastLink; + private boolean mLastIsApp; + private List version; + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.magisk_fragment, container, false); - ButterKnife.bind(this, view); + View v = inflater.inflate(R.layout.magisk_fragment, container, false); + ButterKnife.bind(this, v); new updateUI().execute(); + new CheckUpdates().execute(); - rootToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + return v; + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (requestCode == 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + new DownloadFile(getContext(), mLastLink, mLastIsApp).execute(); + } else { + Toast.makeText(getContext(), R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); + } + } + + private void setupCardView(final boolean app, final String versionCode, final String link, String changelog) { + View clickView; + if (app) { + clickView = appUpdateView; + appCheckUpdatesContainer.setBackgroundColor(blue500); + appCheckUpdatesIcon.setImageResource(R.drawable.ic_file_download); + appCheckUpdatesStatus.setText(R.string.app_update_available); + } else { + clickView = magiskUpdateView; + magiskCheckUpdatesContainer.setBackgroundColor(blue500); + magiskCheckUpdatesIcon.setImageResource(R.drawable.ic_file_download); + magiskCheckUpdatesStatus.setText(R.string.magisk_update_available); + } + + String text = app ? getString(R.string.app_name) : getString(R.string.magisk); + final String msg = getString(R.string.update_available_message, text, versionCode, changelog); + + clickView.setOnClickListener(new View.OnClickListener() { @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - Shell.su(b ? "setprop magisk.root 1" : "setprop magisk.root 0"); - new updateUI().execute(); + public void onClick(View view) { + new AlertDialog.Builder(getContext()) + .setTitle(R.string.update_available) + .setMessage(Html.fromHtml(msg)) + .setCancelable(false) + .setPositiveButton(R.string.update, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + mLastLink = link; + mLastIsApp = app; + + if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED + && Build.VERSION.SDK_INT >= 23) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); + return; + } + + new DownloadFile(getContext(), link, app).execute(); + } + }) + .setNegativeButton(R.string.no_thanks, null) + .show(); } }); - - selinuxToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - Shell.su(b ? "setenforce 1" : "setenforce 0"); - new updateUI().execute(); - } - }); - - return view; } private class updateUI extends AsyncTask { @@ -102,18 +166,7 @@ public class MagiskFragment extends Fragment { progressBar.setVisibility(View.GONE); - magiskStatusView.setVisibility(View.VISIBLE); - rootStatusView.setVisibility(View.VISIBLE); - safetynetStatusView.setVisibility(View.VISIBLE); - selinuxStatusView.setVisibility(View.VISIBLE); - - if (Shell.rootAccess()) { - rootToggleView.setVisibility(View.VISIBLE); - selinuxToggleView.setVisibility(View.VISIBLE); - } - - List selinux = Shell.sh("getenforce"); - List version = Shell.sh("getprop magisk.version"); + version = Shell.sh("getprop magisk.version"); if (version.isEmpty()) { magiskStatusContainer.setBackgroundColor(grey500); @@ -129,91 +182,188 @@ public class MagiskFragment extends Fragment { magiskVersion.setText(getString(R.string.magisk_version, version.get(0))); } - if (selinux.isEmpty()) { - selinuxStatusContainer.setBackgroundColor(grey500); - selinuxStatusIcon.setImageResource(statusUnknown); + magiskStatusView.setVisibility(View.VISIBLE); + } + } - selinuxStatus.setText(R.string.selinux_error_info); - selinuxStatus.setTextColor(grey500); - selinuxToggle.setChecked(false); - } else if (selinux.get(0).equals("Enforcing")) { - selinuxStatusContainer.setBackgroundColor(green500); - selinuxStatusIcon.setImageResource(statusOK); + private class CheckUpdates extends AsyncTask { - selinuxStatus.setText(R.string.selinux_enforcing_info); - selinuxStatus.setTextColor(green500); - selinuxToggle.setChecked(true); - } else { - selinuxStatusContainer.setBackgroundColor(red500); - selinuxStatusIcon.setImageResource(statusError); + @Override + protected String doInBackground(Void... voids) { + try { + HttpURLConnection c = (HttpURLConnection) new URL(JSON_UPDATE_CHECK).openConnection(); + c.setRequestMethod("GET"); + c.setInstanceFollowRedirects(false); + c.setDoOutput(false); + c.connect(); - selinuxStatus.setText(R.string.selinux_permissive_info); - selinuxStatus.setTextColor(red500); - selinuxToggle.setChecked(false); + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + return sb.toString(); + } catch (IOException e) { + return null; + } + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + + appCheckUpdatesProgress.setVisibility(View.GONE); + magiskCheckUpdatesProgress.setVisibility(View.GONE); + + if (result == null) { + appCheckUpdatesContainer.setBackgroundColor(accent); + magiskCheckUpdatesContainer.setBackgroundColor(accent); + + appCheckUpdatesIcon.setImageResource(R.drawable.ic_warning); + magiskCheckUpdatesIcon.setImageResource(R.drawable.ic_warning); + + appCheckUpdatesStatus.setText(R.string.cannot_check_updates); + magiskCheckUpdatesStatus.setText(R.string.cannot_check_updates); + return; } - if (new File("/system/framework/twframework.jar").exists()) { - selinuxToggleView.setVisibility(View.GONE); - selinuxStatus.append("\n" + getString(R.string.selinux_samsung_info)); - } + try { + JSONObject json = new JSONObject(result); - switch (Shell.rootStatus) { - case -1: - // Root Error - rootStatusContainer.setBackgroundColor(grey500); - rootStatusIcon.setImageResource(statusUnknown); - rootStatus.setTextColor(grey500); - rootStatus.setText(R.string.root_error); - rootToggle.setChecked(false); - safetyNetStatusIcon.setImageResource(statusUnknown); - safetyNetStatus.setText(R.string.root_error_info); - break; - case 0: - // Not rooted - rootStatusContainer.setBackgroundColor(green500); - rootStatusIcon.setImageResource(statusOK); - rootStatus.setTextColor(green500); - rootStatus.setText(R.string.root_none); - rootToggle.setChecked(false); - safetyNetStatusIcon.setImageResource(statusOK); - safetyNetStatus.setText(R.string.root_none_info); - break; - case 1: - // Proper root - if (new File("/system/xbin/su").exists()) { - // Mounted - rootStatusContainer.setBackgroundColor(accent); - rootStatusIcon.setImageResource(statusError); - rootStatus.setTextColor(accent); - rootStatus.setText(R.string.root_mounted); - rootToggle.setChecked(true); - safetyNetStatusIcon.setImageResource(statusError); - safetyNetStatus.setText(R.string.root_mounted_info); - break; - } else { - // Not Mounted - rootStatusContainer.setBackgroundColor(green500); - rootStatusIcon.setImageResource(statusOK); - rootStatus.setTextColor(green500); - rootStatus.setText(R.string.root_unmounted); - rootToggle.setChecked(false); - safetyNetStatusIcon.setImageResource(statusOK); - safetyNetStatus.setText(R.string.root_unmounted_info); - break; - } - case 2: - // Improper root - rootStatusContainer.setBackgroundColor(red500); - rootStatusIcon.setImageResource(statusError); - rootStatus.setTextColor(red500); - rootStatus.setText(R.string.root_system); - rootToggle.setChecked(true); - safetyNetStatusIcon.setImageResource(statusError); - safetyNetStatus.setText(R.string.root_system_info); + JSONObject app = json.getJSONObject("app"); + JSONObject magisk = json.getJSONObject("magisk"); - rootToggleView.setVisibility(View.GONE); - break; + String appVersionCode = app.getString("versionCode"); + String appLink = app.getString("link"); + String appChangelog = app.getString("changelog"); + + String magiskVersionCode = magisk.getString("versionCode"); + String magiskLink = magisk.getString("link"); + String magiskChangelog = magisk.getString("changelog"); + + if (Integer.parseInt(appVersionCode) > BuildConfig.VERSION_CODE) { + setupCardView(true, appVersionCode, appLink, appChangelog); + } else { + appCheckUpdatesContainer.setBackgroundColor(green500); + appCheckUpdatesIcon.setImageResource(R.drawable.ic_check_circle); + appCheckUpdatesStatus.setText(getString(R.string.up_to_date, getString(R.string.app_name))); + } + + String v = version.isEmpty() ? "" : version.get(0); + + int versionInt = TextUtils.isEmpty(v) ? 0 : Integer.parseInt(v); + + if (Integer.parseInt(magiskVersionCode) > versionInt) { + setupCardView(false, magiskVersionCode, magiskLink, magiskChangelog); + } else { + magiskCheckUpdatesContainer.setBackgroundColor(green500); + magiskCheckUpdatesIcon.setImageResource(R.drawable.ic_check_circle); + magiskCheckUpdatesStatus.setText(getString(R.string.up_to_date, getString(R.string.magisk))); + } + + } catch (JSONException ignored) { } } } + + private class DownloadFile extends AsyncTask { + + private final Context context; + private final String link; + private final File downloadFile; + private final ProgressDialog progress; + + public DownloadFile(Context context, String link, boolean apk) { + this.link = link; + this.context = context; + + File dir = new File(Environment.getExternalStorageDirectory() + "/Magisk"); + + if (!dir.exists()) dir.mkdir(); + + if (apk) { + downloadFile = new File(dir + "/MagiskManager.apk"); + } else { + downloadFile = new File(dir + "/Magisk.zip"); + } + + Toast.makeText(context, downloadFile.getPath(), Toast.LENGTH_SHORT).show(); + + progress = new ProgressDialog(getContext()); + progress.setTitle(null); + progress.setMessage(getString(R.string.loading)); + progress.setIndeterminate(true); + progress.setCancelable(false); + progress.setButton(android.app.AlertDialog.BUTTON_POSITIVE, getString(android.R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + cancel(true); + } + }); + progress.show(); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + URL u = new URL(link); + URLConnection conn = u.openConnection(); + conn.connect(); + + int length = conn.getContentLength(); + + InputStream input = new BufferedInputStream(u.openStream(), 8192); + OutputStream output = new FileOutputStream(downloadFile); + + byte data[] = new byte[1024]; + long total = 0; + + int count; + while ((count = input.read(data)) != -1) { + total += count; + output.write(data, 0, count); + + publishProgress((int) ((total * 100) / length)); + } + + output.flush(); + + output.close(); + input.close(); + + return true; + } catch (IOException e) { + return false; + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + + progress.setMessage(getString(R.string.loading) + " " + values[0] + "%"); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + progress.dismiss(); + if (!result) { + Toast.makeText(context, R.string.error_download_file, Toast.LENGTH_LONG).show(); + return; + } + + if (downloadFile.getPath().contains("apk")) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(downloadFile), "application/vnd.android.package-archive"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } else { + Toast.makeText(context, R.string.flash_recovery, Toast.LENGTH_LONG).show(); + } + + } + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/RootFragment.java b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java new file mode 100644 index 000000000..9300f276e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java @@ -0,0 +1,199 @@ +package com.topjohnwu.magisk; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.Switch; +import android.widget.TextView; + +import com.topjohnwu.magisk.utils.Shell; + +import java.io.File; +import java.util.List; + +import butterknife.BindColor; +import butterknife.BindView; +import butterknife.ButterKnife; + +public class RootFragment extends Fragment { + + @BindView(R.id.progressBar) ProgressBar progressBar; + + @BindView(R.id.rootSwitchView) View rootToggleView; + @BindView(R.id.selinuxSwitchView) View selinuxToggleView; + @BindView(R.id.rootStatusView) View rootStatusView; + @BindView(R.id.safetynetStatusView) View safetynetStatusView; + @BindView(R.id.selinuxStatusView) View selinuxStatusView; + + @BindView(R.id.root_toggle) Switch rootToggle; + @BindView(R.id.selinux_toggle) Switch selinuxToggle; + + @BindView(R.id.root_status_container) View rootStatusContainer; + @BindView(R.id.root_status_icon) ImageView rootStatusIcon; + @BindView(R.id.root_status) TextView rootStatus; + + @BindView(R.id.selinux_status_container) View selinuxStatusContainer; + @BindView(R.id.selinux_status_icon) ImageView selinuxStatusIcon; + @BindView(R.id.selinux_status) TextView selinuxStatus; + + @BindView(R.id.safety_net_status) TextView safetyNetStatus; + @BindView(R.id.safety_net_icon) ImageView safetyNetStatusIcon; + + @BindColor(R.color.red500) int red500; + @BindColor(R.color.green500) int green500; + @BindColor(R.color.grey500) int grey500; + @BindColor(R.color.accent) int accent; + + int statusOK = R.drawable.ic_check_circle; + int statusError = R.drawable.ic_error; + int statusUnknown = R.drawable.ic_help; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.root_fragment, container, false); + ButterKnife.bind(this, view); + + new updateUI().execute(); + + rootToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + Shell.su(b ? "setprop magisk.root 1" : "setprop magisk.root 0"); + new updateUI().execute(); + } + }); + + selinuxToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + Shell.su(b ? "setenforce 1" : "setenforce 0"); + new updateUI().execute(); + } + }); + + return view; + } + + private class updateUI extends AsyncTask { + + @Override + protected Void doInBackground(Void... voids) { + // Make sure static block invoked + Shell.rootAccess(); + return null; + } + + @Override + protected void onPostExecute(Void v) { + super.onPostExecute(v); + + progressBar.setVisibility(View.GONE); + + rootStatusView.setVisibility(View.VISIBLE); + safetynetStatusView.setVisibility(View.VISIBLE); + selinuxStatusView.setVisibility(View.VISIBLE); + + if (Shell.rootAccess()) { + rootToggleView.setVisibility(View.VISIBLE); + selinuxToggleView.setVisibility(View.VISIBLE); + } + + List selinux = Shell.sh("getenforce"); + + if (selinux.isEmpty()) { + selinuxStatusContainer.setBackgroundColor(grey500); + selinuxStatusIcon.setImageResource(statusUnknown); + + selinuxStatus.setText(R.string.selinux_error_info); + selinuxStatus.setTextColor(grey500); + selinuxToggle.setChecked(false); + } else if (selinux.get(0).equals("Enforcing")) { + selinuxStatusContainer.setBackgroundColor(green500); + selinuxStatusIcon.setImageResource(statusOK); + + selinuxStatus.setText(R.string.selinux_enforcing_info); + selinuxStatus.setTextColor(green500); + selinuxToggle.setChecked(true); + } else { + selinuxStatusContainer.setBackgroundColor(red500); + selinuxStatusIcon.setImageResource(statusError); + + selinuxStatus.setText(R.string.selinux_permissive_info); + selinuxStatus.setTextColor(red500); + selinuxToggle.setChecked(false); + } + + if (new File("/system/framework/twframework.jar").exists()) { + selinuxToggleView.setVisibility(View.GONE); + selinuxStatus.append("\n" + getString(R.string.selinux_samsung_info)); + } + + switch (Shell.rootStatus) { + case -1: + // Root Error + rootStatusContainer.setBackgroundColor(grey500); + rootStatusIcon.setImageResource(statusUnknown); + rootStatus.setTextColor(grey500); + rootStatus.setText(R.string.root_error); + rootToggle.setChecked(false); + safetyNetStatusIcon.setImageResource(statusUnknown); + safetyNetStatus.setText(R.string.root_error_info); + break; + case 0: + // Not rooted + rootStatusContainer.setBackgroundColor(green500); + rootStatusIcon.setImageResource(statusOK); + rootStatus.setTextColor(green500); + rootStatus.setText(R.string.root_none); + rootToggle.setChecked(false); + safetyNetStatusIcon.setImageResource(statusOK); + safetyNetStatus.setText(R.string.root_none_info); + break; + case 1: + // Proper root + if (new File("/system/xbin/su").exists()) { + // Mounted + rootStatusContainer.setBackgroundColor(accent); + rootStatusIcon.setImageResource(statusError); + rootStatus.setTextColor(accent); + rootStatus.setText(R.string.root_mounted); + rootToggle.setChecked(true); + safetyNetStatusIcon.setImageResource(statusError); + safetyNetStatus.setText(R.string.root_mounted_info); + break; + } else { + // Not Mounted + rootStatusContainer.setBackgroundColor(green500); + rootStatusIcon.setImageResource(statusOK); + rootStatus.setTextColor(green500); + rootStatus.setText(R.string.root_unmounted); + rootToggle.setChecked(false); + safetyNetStatusIcon.setImageResource(statusOK); + safetyNetStatus.setText(R.string.root_unmounted_info); + break; + } + case 2: + // Improper root + rootStatusContainer.setBackgroundColor(red500); + rootStatusIcon.setImageResource(statusError); + rootStatus.setTextColor(red500); + rootStatus.setText(R.string.root_system); + rootToggle.setChecked(true); + safetyNetStatusIcon.setImageResource(statusError); + safetyNetStatus.setText(R.string.root_system_info); + + rootToggleView.setVisibility(View.GONE); + selinuxToggleView.setVisibility(View.GONE); + break; + } + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java index f5044a4a2..64dfbbde7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java @@ -1,53 +1,21 @@ package com.topjohnwu.magisk; -import android.Manifest; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; -import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -import android.text.Html; -import android.text.TextUtils; import android.view.MenuItem; import android.view.View; -import android.widget.Toast; - -import com.topjohnwu.magisk.utils.Shell; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.File; -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.net.URLConnection; -import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; @@ -55,7 +23,6 @@ import butterknife.ButterKnife; public class WelcomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private static final String SELECTED_ITEM_ID = "SELECTED_ITEM_ID"; - private static final String JSON_UPDATE_CHECK = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updater/app/magisk_update.json"; private final Handler mDrawerHandler = new Handler(); @@ -66,9 +33,6 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView @IdRes private int mSelectedId = R.id.magisk; - private String mLastLink; - private boolean mLastIsApp; - @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -117,7 +81,6 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView navigationView.setNavigationItemSelectedListener(this); - new CheckUpdates(this).execute(); } @Override @@ -160,6 +123,11 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView tag = "magisk"; navFragment = new MagiskFragment(); break; + case R.id.root: + setTitle(R.string.root); + tag = "root"; + navFragment = new RootFragment(); + break; case R.id.modules: setTitle(R.string.modules); tag = "modules"; @@ -187,216 +155,4 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView } } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - - if (requestCode == 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - new DownloadFile(this, mLastLink, mLastIsApp).execute(); - } else { - Toast.makeText(this, R.string.permissionNotGranted, Toast.LENGTH_LONG).show(); - } - } - - private class CheckUpdates extends AsyncTask { - - private final Context context; - - public CheckUpdates(Context context) { - this.context = context; - } - - @Override - protected String doInBackground(Void... voids) { - try { - HttpURLConnection c = (HttpURLConnection) new URL(JSON_UPDATE_CHECK).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(); - return sb.toString(); - } catch (IOException e) { - return null; - } - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - - if (result == null) return; - - try { - JSONObject json = new JSONObject(result); - - JSONObject app = json.getJSONObject("app"); - JSONObject magisk = json.getJSONObject("magisk"); - - String appVersionCode = app.getString("versionCode"); - String appLink = app.getString("link"); - String appChangelog = app.getString("changelog"); - - String magiskVersionCode = magisk.getString("versionCode"); - String magiskLink = magisk.getString("link"); - String magiskChangelog = magisk.getString("changelog"); - - if (Integer.parseInt(appVersionCode) > BuildConfig.VERSION_CODE) { - showUpdateDialog(true, appVersionCode, appLink, appChangelog); - } - - List versionSh = Shell.sh("getprop magisk.version"); - - String version = versionSh.isEmpty() ? "" : versionSh.get(0); - - int versionInt = TextUtils.isEmpty(version) ? 0 : Integer.parseInt(version); - - if (Integer.parseInt(magiskVersionCode) > versionInt) { - showUpdateDialog(false, magiskVersionCode, magiskLink, magiskChangelog); - } - - } catch (JSONException ignored) { - } - } - - private boolean isUpdateIgnored(String version) { - SharedPreferences prefs = getSharedPreferences(getPackageName() + "_preferences", MODE_PRIVATE); - return prefs.getBoolean("update_ignored_" + version, false); - } - - private void setUpdateIgnored(String version) { - SharedPreferences prefs = getSharedPreferences(getPackageName() + "_preferences", MODE_PRIVATE); - prefs.edit().putBoolean("update_ignored_" + version, true).apply(); - } - - private void showUpdateDialog(final boolean app, final String versionCode, final String link, String changelog) { - if (isUpdateIgnored(versionCode)) return; - - String text = app ? getString(R.string.app_name) : getString(R.string.magisk); - String msg = getString(R.string.update_available_message, text, versionCode, changelog); - - new AlertDialog.Builder(context) - .setTitle(R.string.update_available) - .setMessage(Html.fromHtml(msg)) - .setCancelable(false) - .setPositiveButton(R.string.update, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - mLastLink = link; - mLastIsApp = app; - - if (ActivityCompat.checkSelfPermission(WelcomeActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED - && Build.VERSION.SDK_INT >= 23) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); - return; - } - - new DownloadFile(context, link, app).execute(); - } - }) - .setNegativeButton(R.string.no_thanks, null) - .setNeutralButton(R.string.never_show_again, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - setUpdateIgnored(versionCode); - } - }) - .show(); - } - } - - private class DownloadFile extends AsyncTask { - - private final Context context; - private final String link; - private final File downloadFile; - private final ProgressDialog progress; - - public DownloadFile(Context context, String link, boolean apk) { - this.link = link; - this.context = context; - - File dir = new File(Environment.getExternalStorageDirectory() + "/Magisk"); - - if (!dir.exists()) dir.mkdir(); - - if (apk) { - downloadFile = new File(dir + "/MagiskManager.apk"); - } else { - downloadFile = new File(dir + "/Magisk.zip"); - } - - Toast.makeText(context, downloadFile.getPath(), Toast.LENGTH_SHORT).show(); - - progress = ProgressDialog.show(context, null, getString(R.string.loading), true, false); - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - URL u = new URL(link); - URLConnection conn = u.openConnection(); - conn.connect(); - - int length = conn.getContentLength(); - - InputStream input = new BufferedInputStream(u.openStream(), 8192); - OutputStream output = new FileOutputStream(downloadFile); - - byte data[] = new byte[1024]; - long total = 0; - - int count; - while ((count = input.read(data)) != -1) { - total += count; - output.write(data, 0, count); - - publishProgress((int) ((total * 100) / length)); - } - - output.flush(); - - output.close(); - input.close(); - - return true; - } catch (IOException e) { - return false; - } - } - - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - - progress.setMessage(getString(R.string.loading) + values[0] + "%"); - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - progress.dismiss(); - if (!result) { - Toast.makeText(context, R.string.error_download_file, Toast.LENGTH_LONG).show(); - return; - } - - if (downloadFile.getPath().contains("apk")) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(downloadFile), "application/vnd.android.package-archive"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } else { - Toast.makeText(context, R.string.flash_recovery, Toast.LENGTH_LONG).show(); - } - - } - } } diff --git a/app/src/main/res/drawable/ic_file_download.xml b/app/src/main/res/drawable/ic_file_download.xml new file mode 100644 index 000000000..0db3296cd --- /dev/null +++ b/app/src/main/res/drawable/ic_file_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_warning.xml b/app/src/main/res/drawable/ic_warning.xml new file mode 100644 index 000000000..6af62ea04 --- /dev/null +++ b/app/src/main/res/drawable/ic_warning.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/root.xml b/app/src/main/res/drawable/root.xml new file mode 100644 index 000000000..bca20e4d8 --- /dev/null +++ b/app/src/main/res/drawable/root.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/magisk_fragment.xml b/app/src/main/res/layout/magisk_fragment.xml index 6f0dbc5ba..89c5ec58d 100644 --- a/app/src/main/res/layout/magisk_fragment.xml +++ b/app/src/main/res/layout/magisk_fragment.xml @@ -4,58 +4,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="8dp" - android:layout_marginTop="?attr/actionBarSize"> + android:layout_marginTop="?attr/actionBarSize" + android:orientation="vertical"> - - - - - - - - - - - - - - + app:cardUseCompatPadding="true"> @@ -85,6 +41,12 @@ android:layout_height="84dp" android:layout_gravity="center"/> + + - + app:cardUseCompatPadding="true"> + + - - - - - - - - - - - - + app:cardUseCompatPadding="true"> + + - \ No newline at end of file diff --git a/app/src/main/res/layout/root_fragment.xml b/app/src/main/res/layout/root_fragment.xml new file mode 100644 index 000000000..c143bdcfa --- /dev/null +++ b/app/src/main/res/layout/root_fragment.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml index 5391193d2..398f8b49b 100644 --- a/app/src/main/res/menu/drawer.xml +++ b/app/src/main/res/menu/drawer.xml @@ -10,6 +10,11 @@ android:icon="@drawable/magisk" android:title="@string/magisk"/> + + #F44336 #4CAF50 + #2196F3 #9E9E9E diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84827f905..04a86a4d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -64,5 +64,10 @@ Don\'t show again Error downloading file File downloaded in Magisk directory, you can flash it in recovery + Root + An update for Magisk Manager is available! + An update for Magisk is available! + Cannot check for updates + You\'ve got the latest version available of %1$s