Compare commits

..

36 Commits

Author SHA1 Message Date
topjohnwu
e1aabd70e8 Bump version 3.1 2017-01-11 20:31:42 +08:00
topjohnwu
a9dc1b32e0 Add release notes to install button 2017-01-11 19:18:27 +08:00
topjohnwu
01d847ae4e Improve settings 2017-01-11 19:10:30 +08:00
topjohnwu
61e2c3444a Remove token, use ETag to prevent multiple queries 2017-01-11 17:37:35 +08:00
killer7Mod
5363b0f810 updates for portuguese translation 2017-01-11 15:21:58 +08:00
tonymanou
f0e1a8823e Simplify listeners containing async tasks 2017-01-11 15:20:51 +08:00
tonymanou
7be5937aa0 Using checked state listener instead of click listener 2017-01-11 15:20:51 +08:00
tonymanou
8f43055b0e Fix possible list items displaying wrong information
It is better to display empty strings rather than forget to reset
textviews when a viewholder is reused!
2017-01-11 15:20:51 +08:00
tonymanou
953a81b299 Extract getItem() method from onBindViewHolder() 2017-01-11 15:20:51 +08:00
tonymanou
1d34ae7934 Avoid storing context in adapter, static viewholder, remove useless code 2017-01-11 15:20:51 +08:00
tonymanou
2cabb2666b Avoid possible NPE 2017-01-11 15:20:51 +08:00
tonymanou
0b59bb1a29 Do not let magisk hide's apps list blink 2017-01-11 15:17:49 +08:00
tonymanou
c1e7d74b96 Reapply filter when reloading app list 2017-01-11 15:17:49 +08:00
tonymanou
cc262d6595 Change click listener to checked state listener in magisk hide 2017-01-11 15:17:49 +08:00
tonymanou
61d43b118b Use stricter package name test in magisk hide 2017-01-11 15:17:49 +08:00
tonymanou
989d8181dd Do not store context in magisk hide adapter, remove unused code 2017-01-11 15:17:49 +08:00
tonymanou
cffc157d98 Remove useless mView field from fragments 2017-01-11 15:17:49 +08:00
tonymanou
2a70619577 Improve magisk hide app list's adapter, better thread safety 2017-01-11 15:17:49 +08:00
tonymanou
b91919bffa Use string.xml committers' name as translators 2017-01-10 23:06:41 +08:00
tonymanou
fb7a4bf880 Remove empty dutch string, fix german spelling mistake 2017-01-10 23:06:41 +08:00
tonymanou
4b41799a90 Use references in string-array resources 2017-01-10 23:06:41 +08:00
topjohnwu
123f39a21b We can see the token through logs anyway, no need to encrypt 2017-01-10 22:56:48 +08:00
topjohnwu
cadab12737 Prevent root tasks if no root access 2017-01-10 22:47:58 +08:00
topjohnwu
742055c43b Various small changes 2017-01-10 22:30:05 +08:00
topjohnwu
fa73b41fa7 Update repo and module item layout 2017-01-07 03:18:47 +08:00
topjohnwu
a474eafe84 Improve installation UI and dialog 2017-01-07 02:46:50 +08:00
topjohnwu
442fcf921c Change SafetyNet check to manual start 2017-01-07 01:19:18 +08:00
topjohnwu
fb0923f3ab Magisk Hide fragment improvements 2017-01-07 00:29:53 +08:00
topjohnwu
5bb943f845 Fix repo expand card issue 2017-01-06 15:33:31 +08:00
topjohnwu
a3109953d0 Update README.md 2017-01-06 10:59:00 +08:00
Ahmed Zahrani
ff266c8c79 Update Arabic translation. 2017-01-06 02:51:34 +08:00
tonymanou
ef2e02098d Use untranslatable string when storing theme
This fixes #30
2017-01-06 02:44:07 +08:00
Wang Han
93598d3a51 Fix download button overlay on repo description when expanded 2017-01-06 02:43:16 +08:00
Wang Han
53aebcfb1e Fix MagiskHide Fragment Crash when freshing 2017-01-06 02:43:16 +08:00
Wang Han
bb2467d2ac Handle Google API Connection Problems 2017-01-06 02:43:16 +08:00
gh2923
05c063b61d Update Simplified Chinese Translation 2017-01-06 02:38:09 +08:00
43 changed files with 897 additions and 739 deletions

View File

@@ -1,4 +1,4 @@
# Magisk Manager
The project can only be compiled on Android Studio Version 2.2.0+
I use Java 8 features, which requires Jack compiler and that's only available in 2.2.0+
The project should be built with Android Studio version 2.2.0+
I use Java 8 features, which requires Jack compiler and it's only available in 2.2.0+
Also, you need to install CMake and NDK to build the zipadjust library for zip preprocessing

View File

@@ -8,14 +8,14 @@ android {
applicationId "com.topjohnwu.magisk"
minSdkVersion 21
targetSdkVersion 25
versionCode 11
versionName "3.0"
versionCode 12
versionName "3.1"
jackOptions {
enabled true
}
ndk {
moduleName 'zipadjust'
abiFilters 'x86', 'x86_64', 'armeabi', 'arm64-v8a'
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Html;
@@ -18,6 +18,7 @@ import android.view.WindowManager;
import android.widget.TextView;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import java.io.IOException;
import java.io.InputStream;
@@ -38,14 +39,13 @@ public class AboutActivity extends AppCompatActivity {
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
@BindView(R.id.support_thread) AboutCardRow supportThread;
@BindView(R.id.donation) AboutCardRow donation;
private AlertDialog.Builder builder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (theme.equals("Dark")) {
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
setContentView(R.layout.activity_about);
@@ -76,11 +76,6 @@ public class AboutActivity extends AppCompatActivity {
}
appChangelog.removeSummary();
if (theme.equals("Dark")) {
builder = new AlertDialog.Builder(this, R.style.AlertDialog_dh);
} else {
builder = new AlertDialog.Builder(this);
}
if (changes == null) {
appChangelog.setVisibility(View.GONE);
} else {
@@ -91,13 +86,11 @@ public class AboutActivity extends AppCompatActivity {
result = Html.fromHtml(changes);
}
appChangelog.setOnClickListener(v -> {
AlertDialog d = builder
AlertDialog d = Utils.getAlertDialogBuilder(this)
.setTitle(R.string.app_changelog)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)
.create();
d.show();
.show();
//noinspection ConstantConditions
((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
@@ -112,7 +105,7 @@ public class AboutActivity extends AppCompatActivity {
} else {
result = Html.fromHtml(getString(R.string.app_developers_));
}
AlertDialog d = builder
AlertDialog d = Utils.getAlertDialogBuilder(this)
.setTitle(R.string.app_developers)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)

View File

@@ -1,6 +1,8 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.CardView;
@@ -30,6 +32,7 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
public static List<String> blockList;
public static String bootBlock = null;
@BindView(R.id.current_version_title) TextView currentVersionTitle;
@BindView(R.id.install_title) TextView installTitle;
@BindView(R.id.block_spinner) Spinner spinner;
@BindView(R.id.detect_bootimage) Button detectButton;
@@ -43,15 +46,16 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
View v = inflater.inflate(R.layout.install_fragment, container, false);
ButterKnife.bind(this, v);
detectButton.setOnClickListener(v1 -> toAutoDetect());
currentVersionTitle.setText(getString(R.string.current_magisk_title, StatusFragment.magiskVersionString));
installTitle.setText(getString(R.string.install_magisk_title, StatusFragment.remoteMagiskVersion));
flashButton.setOnClickListener(v1 -> {
String bootImage = bootBlock;
if (bootImage == null) {
bootImage = blockList.get(spinner.getSelectedItemPosition() - 1);
}
String filename = "Magisk-v" + String.valueOf(StatusFragment.remoteMagiskVersion) + ".zip";
String filename = "Magisk-v" + StatusFragment.remoteMagiskVersion + ".zip";
String finalBootImage = bootImage;
MainActivity.alertBuilder
Utils.getAlertDialogBuilder(getActivity())
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
.setMessage(getString(R.string.repo_install_msg, filename))
.setCancelable(true)
@@ -60,6 +64,9 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
new MagiskDlReceiver(finalBootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
StatusFragment.magiskLink,
Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(StatusFragment.releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.show();
});

View File

@@ -1,13 +1,13 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -17,59 +17,66 @@ import android.widget.SearchView;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MagiskHideFragment extends Fragment {
public class MagiskHideFragment extends Fragment implements CallbackHandler.EventListener {
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private PackageManager packageManager;
private View mView;
private List<ApplicationInfo> listApps = new ArrayList<>(), fListApps = new ArrayList<>();
private List<String> hideList = new ArrayList<>();
private ApplicationAdapter appAdapter = new ApplicationAdapter(fListApps, hideList);
// Don't show in list...
public static final List<String> BLACKLIST = Arrays.asList(
"android",
"com.topjohnwu.magisk",
"com.google.android.gms",
"com.google.android.apps.walletnfcrel",
"com.nianticlabs.pokemongo"
);
public static CallbackHandler.Event packageLoadDone = new CallbackHandler.Event();
private ApplicationAdapter appAdapter;
private SearchView.OnQueryTextListener searchListener;
private String lastFilter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.magisk_hide_fragment, container, false);
ButterKnife.bind(this, mView);
View view = inflater.inflate(R.layout.magisk_hide_fragment, container, false);
ButterKnife.bind(this, view);
packageManager = getActivity().getPackageManager();
PackageManager packageManager = getActivity().getPackageManager();
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new LoadApps().exec();
});
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(packageManager).exec());
appAdapter = new ApplicationAdapter(packageManager);
recyclerView.setAdapter(appAdapter);
searchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
lastFilter = query;
appAdapter.filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
new FilterApps().exec(newText);
lastFilter = newText;
appAdapter.filter(newText);
return false;
}
};
new LoadApps().exec();
return mView;
return view;
}
@Override
@@ -83,54 +90,27 @@ public class MagiskHideFragment extends Fragment {
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
mView = this.getView();
getActivity().setTitle(R.string.magiskhide);
}
private class LoadApps extends Async.RootTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
listApps.clear();
hideList.clear();
fListApps.clear();
listApps.addAll(packageManager.getInstalledApplications(PackageManager.GET_META_DATA));
Collections.sort(listApps, (a, b) -> a.loadLabel(packageManager).toString().toLowerCase()
.compareTo(b.loadLabel(packageManager).toString().toLowerCase()));
hideList.addAll(Shell.su(Async.MAGISK_HIDE_PATH + "list"));
fListApps.addAll(listApps);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
updateUI();
CallbackHandler.register(packageLoadDone, this);
if (packageLoadDone.isTriggered) {
onTrigger(packageLoadDone);
}
}
private class FilterApps extends Async.NormalTask<String, Void, Void> {
@Override
protected Void doInBackground(String... strings) {
String newText = strings[0];
fListApps.clear();
for (ApplicationInfo info : listApps) {
if (info.loadLabel(packageManager).toString().toLowerCase().contains(newText.toLowerCase())
|| info.packageName.toLowerCase().contains(newText.toLowerCase())) {
fListApps.add(info);
}
}
return null;
}
@Override
protected void onPostExecute(Void v) {
appAdapter.notifyDataSetChanged();
}
@Override
public void onPause() {
super.onPause();
CallbackHandler.unRegister(packageLoadDone, this);
}
private void updateUI() {
appAdapter.notifyDataSetChanged();
recyclerView.setVisibility(View.VISIBLE);
@Override
public void onTrigger(CallbackHandler.Event event) {
Logger.dev("MagiskHideFragment: UI refresh");
Async.LoadApps.Result result = (Async.LoadApps.Result) event.getResult();
appAdapter.setLists(result.listApps, result.hideList);
mSwipeRefreshLayout.setRefreshing(false);
if (!TextUtils.isEmpty(lastFilter)) {
appAdapter.filter(lastFilter);
}
}
}
}

View File

@@ -17,7 +17,6 @@ import android.support.v4.app.ActivityCompat;
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.view.Menu;
@@ -25,8 +24,8 @@ import android.view.MenuItem;
import android.view.View;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -34,10 +33,10 @@ import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, CallbackHandler.EventListener {
public static AlertDialog.Builder alertBuilder = null;
private static final String SELECTED_ITEM_ID = "SELECTED_ITEM_ID";
public static CallbackHandler.Event recreate = new CallbackHandler.Event();
private final Handler mDrawerHandler = new Handler();
private SharedPreferences prefs;
@@ -53,13 +52,8 @@ public class MainActivity extends AppCompatActivity
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String theme = prefs.getString("theme", "");
Logger.dev("MainActivity: Theme is " + theme);
if (theme.equals("Dark")) {
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
alertBuilder = new AlertDialog.Builder(this, R.style.AlertDialog_dh);
} else {
alertBuilder = new AlertDialog.Builder(this);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@@ -98,24 +92,29 @@ public class MainActivity extends AppCompatActivity
}
navigationView.setNavigationItemSelectedListener(this);
if (StatusFragment.updateCheckDone.isTriggered) {
onTrigger(StatusFragment.updateCheckDone);
}
}
@Override
protected void onResume() {
super.onResume();
CallbackHandler.register(StatusFragment.updateCheckDone, this);
CallbackHandler.register(recreate, this);
if (StatusFragment.updateCheckDone.isTriggered) {
onTrigger(StatusFragment.updateCheckDone);
}
checkHideSection();
}
@Override
protected void onPause() {
super.onPause();
CallbackHandler.unRegister(StatusFragment.updateCheckDone, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(StatusFragment.updateCheckDone, this);
alertBuilder = null;
CallbackHandler.unRegister(recreate, this);
}
@Override
@@ -144,17 +143,25 @@ public class MainActivity extends AppCompatActivity
@Override
public void onTrigger(CallbackHandler.Event event) {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.install).setVisible(StatusFragment.remoteMagiskVersion > 0 &&
Shell.rootAccess());
if (event == StatusFragment.updateCheckDone) {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.install).setVisible(StatusFragment.remoteMagiskVersion > 0 &&
Shell.rootAccess());
} else if (event == recreate) {
recreate();
}
}
private void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(StatusFragment.magiskVersion > 0 &&
menu.findItem(R.id.magiskhide).setVisible(StatusFragment.magiskVersion >= 8 &&
prefs.getBoolean("magiskhide", false) && Shell.rootAccess());
menu.findItem(R.id.modules).setVisible(StatusFragment.magiskVersion > 0);
menu.findItem(R.id.downloads).setVisible(StatusFragment.magiskVersion > 0);
menu.findItem(R.id.modules).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.downloads).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.install).setVisible(Shell.rootAccess());
}
public void navigate(final int itemId) {

View File

@@ -39,13 +39,12 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
@BindView(R.id.fab) FloatingActionButton fabio;
private List<Module> listModules = new ArrayList<>();
private View mView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.modules_fragment, container, false);
ButterKnife.bind(this, mView);
View view = inflater.inflate(R.layout.modules_fragment, container, false);
ButterKnife.bind(this, view);
fabio.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
@@ -74,7 +73,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
updateUI();
}
return mView;
return view;
}
@Override
@@ -96,7 +95,6 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
@Override
public void onResume() {
super.onResume();
mView = this.getView();
CallbackHandler.register(moduleLoadDone, this);
getActivity().setTitle(R.string.modules);
}

View File

@@ -1,10 +1,11 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBar;
@@ -15,6 +16,7 @@ import android.widget.Toast;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
@@ -30,7 +32,7 @@ public class SettingsActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (theme.equals("Dark")) {
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
@@ -86,14 +88,34 @@ public class SettingsActivity extends AppCompatActivity {
CheckBoxPreference busyboxPreference = (CheckBoxPreference) findPreference("busybox");
CheckBoxPreference magiskhidePreference = (CheckBoxPreference) findPreference("magiskhide");
CheckBoxPreference hostsPreference = (CheckBoxPreference) findPreference("hosts");
Preference clear = findPreference("clear");
themePreference.setSummary(themePreference.getValue());
clear.setOnPreferenceClickListener((pref) -> {
SharedPreferences repoMap = getActivity().getSharedPreferences(ModuleHelper.FILE_KEY, Context.MODE_PRIVATE);
repoMap.edit()
.putString(ModuleHelper.ETAG_KEY, "")
.putInt(ModuleHelper.VERSION_KEY, 0)
.apply();
new Async.LoadRepos(getActivity()).exec();
Toast.makeText(getActivity(), R.string.repo_cache_cleared, Toast.LENGTH_LONG).show();
return true;
});
if (Utils.isDarkTheme) {
themePreference.setSummary(R.string.theme_dark);
} else {
themePreference.setSummary(R.string.theme_default);
}
if (StatusFragment.magiskVersion < 9) {
hostsPreference.setEnabled(false);
busyboxPreference.setEnabled(false);
} else if (StatusFragment.magiskVersion < 8) {
magiskhidePreference.setEnabled(false);
} else if (! Shell.rootAccess()) {
busyboxPreference.setEnabled(false);
magiskhidePreference.setEnabled(false);
hostsPreference.setEnabled(false);
} else {
busyboxPreference.setEnabled(true);
magiskhidePreference.setEnabled(true);
@@ -120,81 +142,62 @@ public class SettingsActivity extends AppCompatActivity {
switch (key) {
case "theme":
String theme = prefs.getString(key, "");
themePreference.setSummary(theme);
if (theme.equals("Dark")) {
getActivity().getApplication().setTheme(R.style.AppTheme_dh);
} else {
getActivity().getApplication().setTheme(R.style.AppTheme);
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
if (Utils.isDarkTheme != theme.equalsIgnoreCase(getString(R.string.theme_dark_value))) {
Utils.isDarkTheme = !Utils.isDarkTheme;
getActivity().recreate();
MainActivity.recreate.trigger();
}
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
break;
case "magiskhide":
checked = prefs.getBoolean("magiskhide", false);
if (checked) {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
checked = prefs.getBoolean("magiskhide", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... params) {
if (enable) {
Utils.createFile("/magisk/.core/magiskhide/enable");
return null;
}
}.exec();
} else {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
} else {
Utils.removeItem("/magisk/.core/magiskhide/enable");
return null;
}
}.exec();
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "busybox":
checked = prefs.getBoolean("busybox", false);
if (checked) {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... params) {
if (enable) {
Utils.createFile("/magisk/.core/busybox/enable");
return null;
}
}.exec();
} else {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
} else {
Utils.removeItem("/magisk/.core/busybox/enable");
return null;
}
}.exec();
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "hosts":
checked = prefs.getBoolean("hosts", false);
if (checked) {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Shell.su("cp -af /system/etc/hosts /magisk/.core/hosts");
return null;
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... voids) {
if (enable) {
Shell.su("cp -af /system/etc/hosts /magisk/.core/hosts",
"mount -o bind /magisk/.core/hosts /system/etc/hosts");
} else {
Shell.su("umount -l /system/etc/hosts",
"rm -f /magisk/.core/hosts");
}
}.exec();
} else {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Shell.su("umount -l /system/etc/hosts", "rm -f /magisk/.core/hosts");
return null;
}
}.exec();
}
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
return null;
}
}.exec();
break;
case "developer_logging":
Logger.devLog = prefs.getBoolean("developer_logging", false);

View File

@@ -17,7 +17,11 @@ public class SplashActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplication());
if (prefs.getString("theme", "").equals("Dark")) {
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
Utils.isDarkTheme = theme.equalsIgnoreCase(getString(R.string.theme_dark_value));
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
@@ -34,7 +38,6 @@ public class SplashActivity extends AppCompatActivity {
// Start all async tasks
new Async.GetBootBlocks().exec();
new Async.CheckUpdates().exec();
Async.checkSafetyNet(getApplicationContext());
new Async.LoadModules() {
@Override
protected void onPostExecute(Void v) {
@@ -42,6 +45,7 @@ public class SplashActivity extends AppCompatActivity {
new Async.LoadRepos(getApplicationContext()).exec();
}
}.exec();
new Async.LoadApps(getPackageManager()).exec();
// Start main activity
Intent intent = new Intent(getApplicationContext(), MainActivity.class);

View File

@@ -1,7 +1,10 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
@@ -16,6 +19,7 @@ import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.util.List;
@@ -26,9 +30,11 @@ import butterknife.ButterKnife;
public class StatusFragment extends Fragment implements CallbackHandler.EventListener {
public static double magiskVersion, remoteMagiskVersion = -1;
public static String magiskVersionString, magiskLink, magiskChangelog;
public static String magiskVersionString = "(none)", magiskLink, releaseNoteLink;
public static int SNCheckResult = -1;
private static boolean noDialog = false;
public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event();
public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event();
@@ -56,43 +62,53 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@BindColor(android.R.color.transparent) int trans;
int defaultColor;
static {
checkMagiskInfo();
}
private AlertDialog updateMagisk;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.status_fragment, container, false);
ButterKnife.bind(this, v);
defaultColor = magiskUpdateText.getCurrentTextColor();
mSwipeRefreshLayout.setOnRefreshListener(() -> {
magiskStatusContainer.setBackgroundColor(trans);
magiskStatusIcon.setImageResource(0);
magiskUpdateText.setText(R.string.checking_for_updates);
magiskCheckUpdatesProgress.setVisibility(View.VISIBLE);
magiskUpdateText.setTextColor(defaultColor);
safetyNetProgress.setVisibility(View.GONE);
safetyNetContainer.setBackgroundColor(colorNeutral);
safetyNetIcon.setImageResource(R.drawable.ic_safetynet);
safetyNetStatusText.setText(R.string.safetyNet_check_text);
safetyNetStatusText.setTextColor(defaultColor);
safetyNetDone.isTriggered = false;
noDialog = false;
updateUI();
new Async.CheckUpdates().exec();
});
safetyNetContainer.setOnClickListener(view -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetContainer.setBackgroundColor(trans);
safetyNetIcon.setImageResource(0);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
updateUI();
new Async.CheckUpdates().exec();
Async.checkSafetyNet(getActivity());
});
updateUI();
if (updateCheckDone.isTriggered) {
updateCheckUI();
}
if (safetyNetDone.isTriggered) {
updateSafetyNetUI();
}
if (magiskVersion < 0 && Shell.rootAccess()) {
MainActivity.alertBuilder
if (magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
noDialog = true;
Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisk_title)
.setMessage(R.string.no_magisk_msg)
.setCancelable(true)
@@ -108,6 +124,8 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
.show();
}
updateUI();
return v;
}
@@ -127,12 +145,18 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
super.onResume();
CallbackHandler.register(updateCheckDone, this);
CallbackHandler.register(safetyNetDone, this);
if (updateCheckDone.isTriggered) {
updateCheckUI();
}
if (safetyNetDone.isTriggered) {
updateSafetyNetUI();
}
getActivity().setTitle(R.string.status);
}
@Override
public void onDestroy() {
super.onDestroy();
public void onPause() {
super.onPause();
CallbackHandler.unRegister(updateCheckDone, this);
CallbackHandler.unRegister(safetyNetDone, this);
}
@@ -215,12 +239,48 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
magiskCheckUpdatesProgress.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false);
updateMagisk = Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.magisk_update_title)
.setMessage(getString(R.string.magisk_update_message, remoteMagiskVersion))
.setCancelable(true)
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
try {
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
} catch (IllegalStateException ignored) {}
})
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.create();
if (magiskVersion < remoteMagiskVersion && Shell.rootAccess()) {
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
if (!noDialog) {
noDialog = true;
updateMagisk.show();
}
}
}
private void updateSafetyNetUI() {
int image, color;
safetyNetProgress.setVisibility(View.GONE);
switch (SNCheckResult) {
case -3:
color = colorNeutral;
image = R.drawable.ic_help;
safetyNetStatusText.setText(R.string.safetyNet_connection_suspended);
break;
case -2:
color = colorNeutral;
image = R.drawable.ic_help;
safetyNetStatusText.setText(R.string.safetyNet_connection_failed);
break;
case -1:
color = colorNeutral;
image = R.drawable.ic_help;

View File

@@ -1,22 +1,22 @@
package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Utils;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
@@ -24,31 +24,26 @@ import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
private List<ApplicationInfo> mList;
private List<ApplicationInfo> mOriginalList, mList;
private List<String> mHideList;
private Context context;
private PackageManager packageManager;
private ApplicationFilter filter;
// Don't show in list...
private static final String[] blackList = {
"com.topjohnwu.magisk",
"com.google.android.gms"
};
public ApplicationAdapter(PackageManager packageManager) {
mOriginalList = mList = Collections.emptyList();
mHideList = Collections.emptyList();
this.packageManager = packageManager;
}
public ApplicationAdapter(List<ApplicationInfo> list, List<String> hideList) {
mList = list;
mHideList = hideList;
List<String> bl = Arrays.asList(blackList);
for (int i = 0; i < mList.size(); ++i)
if (bl.contains(mList.get(i).packageName))
mList.remove(i);
public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
mOriginalList = mList = Collections.unmodifiableList(listApps);
mHideList = new ArrayList<>(hideList);
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
context = parent.getContext();
packageManager = context.getPackageManager();
ButterKnife.bind(this, mView);
return new ViewHolder(mView);
}
@@ -60,22 +55,14 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
holder.appIcon.setImageDrawable(info.loadIcon(packageManager));
holder.appName.setText(info.loadLabel(packageManager));
holder.appPackage.setText(info.packageName);
holder.checkBox.setChecked(false);
for (String hidePackage : mHideList) {
if (info.packageName.contains(hidePackage)) {
holder.checkBox.setChecked(true);
break;
}
}
holder.checkBox.setOnClickListener(v -> {
CheckBox chkbox = (CheckBox) v;
if (chkbox.isChecked()) {
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) {
new Async.MagiskHide().add(info.packageName);
mHideList.add(info.packageName);
}
else {
} else {
new Async.MagiskHide().rm(info.packageName);
mHideList.remove(info.packageName);
}
@@ -87,19 +74,55 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
public void filter(String constraint) {
if (filter == null) {
filter = new ApplicationFilter();
}
filter.filter(constraint);
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.app_package) TextView appPackage;
@BindView(R.id.checkbox) CheckBox checkBox;
public ViewHolder(View itemView) {
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);
}
}
}
private class ApplicationFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<ApplicationInfo> filteredApps;
if (constraint == null || constraint.length() == 0) {
filteredApps = mOriginalList;
} else {
filteredApps = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : mOriginalList) {
if (Utils.lowercaseContains(info.loadLabel(packageManager), filter)
|| Utils.lowercaseContains(info.packageName, filter)) {
filteredApps.add(info);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredApps;
results.count = filteredApps.size();
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mList = (List<ApplicationInfo>) results.values;
notifyDataSetChanged();
}
}
}

View File

@@ -3,11 +3,10 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
@@ -25,7 +24,6 @@ import butterknife.ButterKnife;
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
private final List<Module> mList;
private Context context;
public ModulesAdapter(List<Module> list) {
mList = list;
@@ -34,91 +32,61 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
context = parent.getContext();
ButterKnife.bind(this, view);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
final Module module = mList.get(position);
holder.title.setText(module.getName());
holder.versionName.setText(module.getVersion());
String author = module.getAuthor();
String versionName = module.getVersion();
String description = module.getDescription();
if (versionName != null) {
holder.versionName.setText(versionName);
}
if (author != null) {
holder.author.setText(context.getString(R.string.author, author));
}
if (description != null) {
holder.description.setText(description);
}
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(module.getDescription());
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
holder.checkBox.setOnClickListener((v) -> {
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked()) {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
module.removeDisableFile();
return null;
}
@Override
protected void onPostExecute(Void v) {
Snackbar.make(holder.title, R.string.disable_file_removed, Snackbar.LENGTH_SHORT).show();
}
}.exec();
} else {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
module.createDisableFile();
return null;
}
@Override
protected void onPostExecute(Void v) {
Snackbar.make(holder.title, R.string.disable_file_created, Snackbar.LENGTH_SHORT).show();
}
}.exec();
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
if (isChecked) {
module.removeDisableFile();
} else {
module.createDisableFile();
}
return null;
}
});
holder.delete.setOnClickListener(v -> {
if (module.willBeRemoved()) {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
module.deleteRemoveFile();
return null;
}
@Override
protected void onPostExecute(Void v) {
Snackbar.make(holder.title, R.string.remove_file_deleted, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec();
} else {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
module.createRemoveFile();
return null;
}
@Override
protected void onPostExecute(Void v) {
Snackbar.make(holder.title, R.string.remove_file_created, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec();
@Override
protected void onPostExecute(Void v) {
int title = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
}
});
}.exec());
holder.delete.setOnClickListener(v -> new Async.RootTask<Void, Void, Void>() {
private final boolean removed = module.willBeRemoved();
@Override
protected Void doInBackground(Void... voids) {
if (removed) {
module.deleteRemoveFile();
} else {
module.createRemoveFile();
}
return null;
}
@Override
protected void onPostExecute(Void v) {
int title = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec());
if (module.isUpdated()) {
holder.notice.setVisibility(View.VISIBLE);
@@ -144,7 +112,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@@ -154,12 +122,9 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@BindView(R.id.author) TextView author;
@BindView(R.id.delete) ImageView delete;
public ViewHolder(View itemView) {
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);
if (!Shell.rootAccess()) {
checkBox.setEnabled(false);

View File

@@ -7,23 +7,22 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.receivers.RepoDlReceiver;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebWindow;
import java.util.HashSet;
import java.util.List;
import butterknife.BindView;
@@ -32,8 +31,7 @@ import butterknife.ButterKnife;
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
private View mView;
private Context mContext;
private HashSet<Repo> expandList = new HashSet<>();
public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
mUpdateRepos = update;
@@ -43,71 +41,62 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
ButterKnife.bind(this, mView);
mContext = parent.getContext();
return new ViewHolder(mView);
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
ButterKnife.bind(this, v);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final Repo repo;
if (position >= mUpdateRepos.size()) {
position -= mUpdateRepos.size();
if (position >= mInstalledRepos.size()) {
position -= mInstalledRepos.size();
repo = mOthersRepos.get(position);
} else {
repo = mInstalledRepos.get(position);
}
} else {
repo = mUpdateRepos.get(position);
}
Context context = holder.itemView.getContext();
Repo repo = getItem(position);
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String author = repo.getAuthor();
String versionName = repo.getVersion();
String description = repo.getDescription();
if (versionName != null) {
holder.versionName.setText(versionName);
}
if (author != null) {
holder.author.setText(mContext.getString(R.string.author, author));
}
if (description != null) {
holder.description.setText(description);
}
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
View.OnClickListener listener = view -> {
if (view.getId() == holder.updateImage.getId()) {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
MainActivity.alertBuilder
.setTitle(mContext.getString(R.string.repo_install_title, repo.getName()))
.setMessage(mContext.getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
mContext,
new RepoDlReceiver(),
repo.getZipUrl(),
Utils.getLegalFilename(filename)))
.setNegativeButton(R.string.no_thanks, null)
.show();
}
if ((view.getId() == holder.changeLog.getId()) && (!repo.getLogUrl().equals(""))) {
new WebWindow(mContext.getString(R.string.changelog), repo.getLogUrl(), mContext);
}
if ((view.getId() == holder.authorLink.getId()) && (!repo.getSupportUrl().equals(""))) {
mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getDonateUrl())));
}
if ((view.getId() == holder.supportLink.getId()) && (!repo.getSupportUrl().equals(""))) {
mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getSupportUrl())));
}
};
holder.setExpanded(expandList.contains(repo));
holder.changeLog.setOnClickListener(listener);
holder.updateImage.setOnClickListener(listener);
holder.authorLink.setOnClickListener(listener);
holder.supportLink.setOnClickListener(listener);
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(repo);
} else {
holder.expand();
expandList.add(repo);
}
});
holder.changeLog.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getLogUrl())) {
new WebWindow(context.getString(R.string.changelog), repo.getLogUrl(), context);
}
});
holder.updateImage.setOnClickListener(view -> {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
Utils.getAlertDialogBuilder(context)
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
context,
new RepoDlReceiver(),
repo.getZipUrl(),
Utils.getLegalFilename(filename)))
.setNegativeButton(R.string.no_thanks, null)
.show();
});
holder.authorLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getDonateUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getDonateUrl())));
}
});
holder.supportLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getSupportUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getSupportUrl())));
}
});
}
@Override
@@ -115,7 +104,21 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
return mUpdateRepos.size() + mInstalledRepos.size() + mOthersRepos.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private Repo getItem(int position) {
if (position >= mUpdateRepos.size()) {
position -= mUpdateRepos.size();
if (position >= mInstalledRepos.size()) {
position -= mInstalledRepos.size();
return mOthersRepos.get(position);
} else {
return mInstalledRepos.get(position);
}
} else {
return mUpdateRepos.get(position);
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@@ -123,68 +126,68 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
@BindView(R.id.author) TextView author;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.update) ImageView updateImage;
@BindView(R.id.installed) ImageView installedImage;
@BindView(R.id.changeLog) ImageView changeLog;
@BindView(R.id.authorLink) ImageView authorLink;
@BindView(R.id.supportLink) ImageView supportLink;
private ValueAnimator mAnimator;
private ObjectAnimator animY2;
private ViewHolder holder;
private boolean mExpanded = false;
private static int expandHeight = 0;
private boolean expanded = false;
public ViewHolder(View itemView) {
ViewHolder(View itemView) {
super(itemView);
WindowManager windowmanager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
ButterKnife.bind(this, itemView);
DisplayMetrics dimension = new DisplayMetrics();
windowmanager.getDefaultDisplay().getMetrics(dimension);
holder = this;
this.expandLayout.getViewTreeObserver().addOnPreDrawListener(
expandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
holder.expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
holder.expandLayout.setVisibility(View.GONE);
holder.expandLayout.measure(widthSpec, heightSpec);
final int holderHeight = holder.expandLayout.getMeasuredHeight();
mAnimator = slideAnimator(0, holderHeight);
animY2 = ObjectAnimator.ofFloat(holder.updateImage, "translationY", holderHeight / 2);
if (expandHeight == 0) {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
expandLayout.measure(widthSpec, heightSpec);
expandHeight = expandLayout.getMeasuredHeight();
}
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
expandLayout.setVisibility(View.GONE);
mAnimator = slideAnimator(0, expandHeight);
animY2 = ObjectAnimator.ofFloat(updateImage, "translationY", expandHeight / 2);
return true;
}
});
mView.setOnClickListener(view -> {
if (expanded) {
collapse(holder.expandLayout);
} else {
expand(holder.expandLayout);
}
expanded = !expanded;
});
}
private void expand(View view) {
view.setVisibility(View.VISIBLE);
private void setExpanded(boolean expanded) {
mExpanded = expanded;
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = expanded ? expandHeight : 0;
expandLayout.setLayoutParams(layoutParams);
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
if (expanded) {
updateImage.setTranslationY(expandHeight / 2);
} else {
updateImage.setTranslationY(0);
}
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
animY2.start();
mExpanded = true;
}
private void collapse(View view) {
int finalHeight = view.getHeight();
private void collapse() {
if (!mExpanded) return;
int finalHeight = expandLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
view.setVisibility(View.GONE);
expandLayout.setVisibility(View.GONE);
}
@Override
@@ -198,7 +201,7 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
});
mAnimator.start();
animY2.reverse();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
@@ -207,8 +210,7 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout
.getLayoutParams();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});

View File

@@ -4,7 +4,7 @@ import android.content.Context;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.WebRequest;
import com.topjohnwu.magisk.utils.WebService;
import java.util.Date;
@@ -22,7 +22,7 @@ public class Repo extends BaseModule {
public void update() throws CacheModException {
Logger.dev("Repo: Re-fetch prop");
String props = WebRequest.makeWebServiceCall(mManifestUrl, WebRequest.GET, true);
String props = WebService.request(mManifestUrl, WebService.GET, true);
String lines[] = props.split("\\n");
parseProps(lines);
}

View File

@@ -2,6 +2,8 @@ package com.topjohnwu.magisk.utils;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
@@ -10,7 +12,7 @@ import android.util.Log;
import android.widget.Toast;
import com.topjohnwu.magisk.InstallFragment;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.MagiskHideFragment;
import com.topjohnwu.magisk.ModulesFragment;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ReposFragment;
@@ -25,6 +27,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class Async {
@@ -32,6 +36,7 @@ public class Async {
public abstract static class RootTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
@SafeVarargs
public final void exec(Params... params) {
if (!Shell.rootAccess()) return;
executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);
}
}
@@ -51,15 +56,13 @@ public class Async {
@Override
protected Void doInBackground(Void... voids) {
String jsonStr = WebRequest.makeWebServiceCall(UPDATE_JSON, WebRequest.GET);
String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
try {
JSONObject json = new JSONObject(jsonStr);
JSONObject magisk = json.getJSONObject("magisk");
StatusFragment.remoteMagiskVersion = magisk.getDouble("versionCode");
StatusFragment.magiskLink = magisk.getString("link");
StatusFragment.magiskChangelog = magisk.getString("changelog");
StatusFragment.releaseNoteLink = magisk.getString("note");
} catch (JSONException ignored) {}
return null;
}
@@ -114,6 +117,45 @@ public class Async {
}
}
public static class LoadApps extends RootTask<Void, Void, LoadApps.Result> {
private PackageManager pm;
public LoadApps(PackageManager packageManager) {
pm = packageManager;
}
@Override
protected Result doInBackground(Void... voids) {
List<ApplicationInfo> listApps = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (Iterator<ApplicationInfo> i = listApps.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (MagiskHideFragment.BLACKLIST.contains(info.packageName) || !info.enabled)
i.remove();
}
Collections.sort(listApps, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
List<String> hideList = Shell.su(Async.MAGISK_HIDE_PATH + "list");
return new Result(listApps, hideList);
}
@Override
protected void onPostExecute(Result result) {
MagiskHideFragment.packageLoadDone.trigger(result);
}
public static class Result {
public final List<ApplicationInfo> listApps;
public final List<String> hideList;
Result(List<ApplicationInfo> listApps, List<String> hideList) {
this.listApps = listApps;
this.hideList = hideList;
}
}
}
public static class FlashZIP extends RootTask<Void, String, Integer> {
protected Uri mUri;
@@ -206,27 +248,20 @@ public class Async {
return -1;
}
if (!unzipAndCheck()) return 0;
if (Shell.rootAccess()) {
publishProgress(mContext.getString(R.string.zip_install_progress_msg, mFilename));
ret = Shell.su(
"BOOTMODE=true sh " + mCachedFile.getParent() +
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
);
Logger.dev("FlashZip: Console log:");
for (String line : ret) {
Logger.dev(line);
}
Shell.su(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + TMP_FOLDER_PATH
);
} else {
if (mCachedFile != null && mCachedFile.exists() && !mCachedFile.delete()) {
Utils.removeItem(mCachedFile.getPath());
}
return -1;
publishProgress(mContext.getString(R.string.zip_install_progress_msg, mFilename));
ret = Shell.su(
"BOOTMODE=true sh " + mCachedFile.getParent() +
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
);
Logger.dev("FlashZip: Console log:");
for (String line : ret) {
Logger.dev(line);
}
Shell.su(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + TMP_FOLDER_PATH
);
if (Boolean.parseBoolean(ret.get(ret.size() - 1))) {
return 1;
}
@@ -257,7 +292,7 @@ public class Async {
StatusFragment.updateCheckDone.trigger();
new LoadModules().exec();
MainActivity.alertBuilder
Utils.getAlertDialogBuilder(mContext)
.setTitle(R.string.reboot_title)
.setMessage(R.string.reboot_msg)
.setPositiveButton(R.string.reboot, (dialogInterface1, i) -> Shell.sh("su -c reboot"))

View File

@@ -33,11 +33,23 @@ public class CallbackHandler {
}
public static class Event {
public boolean isTriggered = false;
private Object result;
public void trigger() {
trigger(null);
}
public void trigger(Object result) {
this.result = result;
isTriggered = true;
triggerCallback(this);
}
public Object getResult() {
return result;
}
}
public interface EventListener {

View File

@@ -28,9 +28,10 @@ import java.util.Map;
public class ModuleHelper {
private static final String MAGISK_PATH = "/magisk";
private static final String FILE_KEY = "RepoMap";
public static final String FILE_KEY = "RepoMap";
private static final String REPO_KEY = "repomap";
private static final String VERSION_KEY = "version";
public static final String VERSION_KEY = "version";
public static final String ETAG_KEY = "ETag";
private static final int DATABASE_VER = 1;
private static ValueSortedMap<String, Repo> repoMap = new ValueSortedMap<>();
@@ -57,10 +58,11 @@ public class ModuleHelper {
public static void createRepoMap(Context context) {
Logger.dev("ModuleHelper: Loading repos");
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
repoMap.clear();
Gson gson = new Gson();
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
String jsonString;
int cachedVersion = prefs.getInt(VERSION_KEY, 0);
@@ -74,20 +76,31 @@ public class ModuleHelper {
ValueSortedMap<String, Repo> cached = null;
if (jsonString != null) {
cached = gson.fromJson(jsonString, new TypeToken< ValueSortedMap<String, Repo> >(){}.getType());
cached = gson.fromJson(jsonString, new TypeToken<ValueSortedMap<String, Repo>>(){}.getType());
}
if (cached == null) {
cached = new ValueSortedMap<>();
}
// Making a request to url and getting response
jsonString = WebRequest.makeWebServiceCall(context.getString(R.string.url_main, Utils.getToken()), WebRequest.GET);
// Get cached ETag to add in the request header
String etag = prefs.getString(ETAG_KEY, "");
HashMap<String, String> header = new HashMap<>();
header.put("If-None-Match", etag);
if (jsonString != null && !jsonString.isEmpty()) {
// Have internet access
// Making a request to main URL for repo info
jsonString = WebService.request(
context.getString(R.string.url_main), WebService.GET, null, header, false);
if (!jsonString.isEmpty()) {
try {
JSONArray jsonArray = new JSONArray(jsonString);
// If it gets to this point, the response is valid, update ETag
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
// Update repo info
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonobject = jsonArray.getJSONObject(i);
String id = jsonobject.getString("description");
@@ -106,7 +119,7 @@ public class ModuleHelper {
Logger.dev("ModuleHelper: Create new repo " + id);
repo = new Repo(context, name, updatedDate);
} else {
Logger.dev("ModuleHelper: Cached repo " + id);
Logger.dev("ModuleHelper: Update cached repo " + id);
repo.update(updatedDate);
}
if (repo.getId() != null) {
@@ -118,13 +131,15 @@ public class ModuleHelper {
e.printStackTrace();
}
} else {
// Use cached if no internet
// Use cached if no internet or no updates
Logger.dev("ModuleHelper: No updates, use cached");
repoMap.putAll(cached);
}
prefs.edit()
.putInt(VERSION_KEY, DATABASE_VER)
.putString(REPO_KEY, gson.toJson(repoMap))
.putString(ETAG_KEY, etag)
.apply();
Logger.dev("ModuleHelper: Repo load done");

View File

@@ -32,6 +32,7 @@ public abstract class SafetyNetHelper
@Override
public void onConnectionFailed(@NonNull ConnectionResult result) {
Logger.dev("SN: Google API fail");
handleResults(-2);
}
@Override
@@ -43,6 +44,7 @@ public abstract class SafetyNetHelper
@Override
public void onConnectionSuspended(int i) {
Logger.dev("SN: Google API Suspended");
handleResults(-3);
}
public void requestTest() {

View File

@@ -1,6 +1,7 @@
package com.topjohnwu.magisk.utils;
import android.Manifest;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.Context;
import android.content.IntentFilter;
@@ -8,7 +9,7 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.util.Base64;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.R;
@@ -17,26 +18,12 @@ import com.topjohnwu.magisk.receivers.DownloadReceiver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class Utils {
public static boolean isDownloading = false;
private static final String cryptoPass = "MagiskRox666";
private static final String secret = "GTYybRBTYf5his9kQ16ZNO7qgkBJ/5MyVe4CGceAOIoXgSnnk8FTd4F1dE9p5Eus";
public static boolean isDarkTheme;
public static boolean itemExist(String path) {
return itemExist(true, path);
@@ -72,11 +59,7 @@ public class Utils {
public static List<String> getModList(String path) {
List<String> ret;
String command = "find " + path + " -type d -maxdepth 1 ! -name \"*.core\" ! -name \"*lost+found\" ! -name \"*magisk\"";
if (Shell.rootAccess()) {
ret = Shell.su(command);
} else {
ret = Shell.sh(command);
}
ret = Shell.su(command);
return ret;
}
@@ -120,29 +103,6 @@ public class Utils {
context.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
public static String getToken() {
try {
DESKeySpec keySpec = new DESKeySpec(cryptoPass.getBytes("UTF8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
byte[] encrypedPwdBytes = Base64.decode(secret, Base64.DEFAULT);
// cipher is not thread safe
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));
return new String(decrypedValueBytes);
} catch (InvalidKeyException | UnsupportedEncodingException | NoSuchAlgorithmException
| BadPaddingException | NoSuchPaddingException | IllegalBlockSizeException
| InvalidKeySpecException e) {
e.printStackTrace();
}
return secret;
}
public static String getLegalFilename(CharSequence filename) {
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("(", "").replace(")", "")
@@ -164,6 +124,18 @@ public class Utils {
return null;
}
public static AlertDialog.Builder getAlertDialogBuilder(Context context) {
if (isDarkTheme) {
return new AlertDialog.Builder(context, R.style.AlertDialog_dh);
} else {
return new AlertDialog.Builder(context);
}
}
public static boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
}
public static class ByteArrayInOutStream extends ByteArrayOutputStream {
public ByteArrayInputStream getInputStream() {
ByteArrayInputStream in = new ByteArrayInputStream(buf, 0, count);

View File

@@ -8,21 +8,17 @@ import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
public class WebRequest {
public class WebService {
static String response = null;
public final static int GET = 1;
public final static int POST = 2;
//Constructor with no parameter
public WebRequest() {
}
private static Map<String, List<String>> responseHeader;
/**
* Making web service call
@@ -30,15 +26,12 @@ public class WebRequest {
* @url - url to make request
* @requestmethod - http request method
*/
public static String makeWebServiceCall(String url, int requestmethod) {
return makeWebServiceCall(url, requestmethod, null, false);
public static String request(String url, int method) {
return request(url, method, null, null, false);
}
public static String makeWebServiceCall(String url, int requestmethod, boolean addNewLines) {
return makeWebServiceCall(url, requestmethod, null, addNewLines);
public static String request(String url, int method, boolean newline) {
return request(url, method, null, null, newline);
}
/**
@@ -47,26 +40,35 @@ public class WebRequest {
* @url - url to make request
* @requestmethod - http request method
* @params - http request params
* @header - http request header
* @newline - true to append a newline each line
*/
public static String makeWebServiceCall(String urladdress, int requestmethod,
HashMap<String, String> params, boolean addNewLines) {
Logger.dev("WebRequest: Service call " + urladdress);
public static String request(String urlAddress, int method,
Map<String, String> params, Map<String, String> header,
boolean newline) {
Logger.dev("WebService: Service call " + urlAddress);
URL url;
String response = "";
StringBuilder response = new StringBuilder();
try {
url = new URL(urladdress);
url = new URL(urlAddress);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.setDoInput(true);
if (requestmethod == POST) {
if (method == POST) {
conn.setRequestMethod("POST");
} else if (requestmethod == GET) {
} else if (method == GET) {
conn.setRequestMethod("GET");
}
if (header != null) {
for (Map.Entry<String, String> entry : header.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
}
if (params != null) {
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
@@ -98,20 +100,25 @@ public class WebRequest {
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line = br.readLine()) != null) {
if (addNewLines) {
response += line + "\n";
if (newline) {
response.append(line).append("\n");
} else {
response += line;
response.append(line);
}
}
responseHeader = conn.getHeaderFields();
} else {
response = "";
responseHeader = null;
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
return response.toString();
}
public static Map<String, List<String>> getLastResponseHeader() {
return responseHeader;
}
}

View File

@@ -1,24 +1,15 @@
package com.topjohnwu.magisk.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.topjohnwu.magisk.R;
public class WebWindow {
public WebWindow(String title, String url, Context context) {
AlertDialog.Builder alert;
String theme = PreferenceManager.getDefaultSharedPreferences(context).getString("theme", "");
if (theme.equals("Dark")) {
alert = new AlertDialog.Builder(context, R.style.AlertDialog_dh);
} else {
alert = new AlertDialog.Builder(context);
}
AlertDialog.Builder alert = Utils.getAlertDialogBuilder(context);
alert.setTitle(title);
Logger.dev("WebView: URL = " + url);

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#fff"
android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z"/>
</vector>

View File

@@ -17,14 +17,30 @@
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/install_title"
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="15dp"
android:textStyle="bold" />
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/install_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
<TextView
android:id="@+id/current_version_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@@ -20,9 +20,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="@dimen/card_layout_padding"
android:paddingEnd="@dimen/card_layout_padding"
android:paddingStart="@dimen/card_layout_padding">
android:padding="@dimen/card_layout_padding">
<TextView
android:id="@+id/title"

View File

@@ -20,133 +20,128 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="@dimen/card_layout_padding">
<RelativeLayout
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<TextView
android:id="@+id/title"
android:layout_width="@dimen/card_textview_width"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="0dp"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="false"/>
<TextView
android:id="@+id/version_name"
android:layout_width="@dimen/card_textview_width"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/title"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/author"
android:layout_width="@dimen/card_textview_width"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/version_name"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/author"
android:layout_gravity="center_vertical"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="false"/>
<ImageView
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/title"
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:background="@drawable/ic_file_download_black"
android:backgroundTint="@color/icon_grey"
android:focusable="false"
android:gravity="end"/>
android:layout_gravity="center_vertical"
android:id="@+id/info_layout">
<LinearLayout
android:id="@+id/expand_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/description"
android:orientation="vertical">
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
android:layout_weight="1">
<ImageView
android:id="@+id/changeLog"
android:layout_width="wrap_content"
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_changelog"
android:tint="@color/icon_grey"/>
android:layout_marginTop="0dp"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="false"/>
<ImageView
android:id="@+id/authorLink"
android:layout_width="wrap_content"
<TextView
android:id="@+id/version_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_person"
android:tint="@color/icon_grey"/>
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<ImageView
android:id="@+id/supportLink"
android:layout_width="wrap_content"
<TextView
android:id="@+id/author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_help"
android:tint="@color/icon_grey"/>
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="false" />
</LinearLayout>
<ImageView
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/ic_file_download_black"
android:backgroundTint="@color/icon_grey"
android:focusable="false"
android:gravity="end" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<ImageView
android:id="@+id/installed"
android:layout_width="wrap_content"
<LinearLayout
android:id="@+id/expand_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:focusable="false"
android:gravity="end"
android:visibility="gone"/>
android:layout_alignParentStart="true"
android:layout_below="@id/info_layout"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/changeLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_changelog"
android:tint="@color/icon_grey"/>
<ImageView
android:id="@+id/authorLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_person"
android:tint="@color/icon_grey"/>
<ImageView
android:id="@+id/supportLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_help"
android:tint="@color/icon_grey"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -157,19 +157,22 @@
android:layout_width="match_parent"
android:layout_height="100dp"
android:foregroundGravity="center"
android:orientation="vertical">
android:orientation="vertical"
android:background="@color/grey500">
<ImageView
android:id="@+id/safetyNet_icon"
android:layout_width="84dp"
android:layout_height="84dp"
android:layout_gravity="center"/>
android:layout_gravity="center"
android:src="@drawable/ic_safetynet"/>
<ProgressBar
android:id="@+id/safetyNet_check_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
@@ -181,7 +184,7 @@
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"
android:text="@string/checking_safetyNet_status" />
android:text="@string/safetyNet_check_text" />
</LinearLayout>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"إفتراضي"</item>
<item>"غامق"</item>
</string-array>
</resources>

View File

@@ -51,7 +51,7 @@
<string name="app_developers">المطورين الرئيسيين</string>
<string name="app_developers_"><![CDATA[التطبيق إنشئ بواسطة <a href="https://github.com/topjohnwu">topjohnwu</a> بالتعاون مع <a href="https://github.com/d8ahazard">Digitalhigh</a> و <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">تغييرات التطبيق</string>
<string name="translators"/>
<string name="translators">xx6600xx ,silent_6600</string>
<string name="app_version">إصدار التطبيق</string>
<string name="app_source_code">الشفرة المصدرية</string>
<string name="donation">التبرع</string>
@@ -85,6 +85,8 @@
<string name="settings_general_category">عام</string>
<string name="settings_theme_title">السمة</string>
<string name="settings_theme_summary">أختر سمة</string>
<string name="theme_default">إفتراضي</string>
<string name="theme_dark">غامق</string>
<string name="settings_magiskhide_title">تمكين إخفاء Magisk</string>
<string name="settings_magiskhide_summary">إخفاء Magisk من مختلف حالات الإكتشاف</string>
@@ -100,5 +102,25 @@
<string name="settings_shell_logging_summary">حدد هذا الخيار لتمكين سجل جميع الأوامر الدفعية والمخرجات</string>
<string name="settings_reboot_toast">إعادة التشغيل لتطبيق الإعدادات</string>
<string name="auto_detect">\"(تلقائي) %1$s\"</string>
<string name="checking_for_updates">البحث عن تحديثات…</string>
<string name="install">التثبيت</string>
<string name="not_rooted">غير مروت</string>
<string name="proper_root">مروت فعلاً</string>
<string name="advanced_settings_title">إعدادات متقدمة</string>
<string name="boot_image_title">موقع ملف الإقلاع</string>
<string name="checking_safetyNet_status">التحقق من حالة SafetyNet...</string>
<string name="copying_msg">نسخ الملف المضغوط إلى دليل مؤقت</string>
<string name="detect_button">تحقق</string>
<string name="downloading_toast">جاري التنزيل %1$s</string>
<string name="install_magisk_title">تثبيت Magisk الإصدار: v%1$.1f</string>
<string name="keep_force_encryption">إبقاء التشفير القوى</string>
<string name="keep_dm_verity">إبقاء dm-verity</string>
<string name="root_error">مروت لكن لا يوجد إذن الروت، غير مسموح به؟</string>
<string name="root_info_warning">وظائف محدودة إلى حد كبير</string>
<string name="safetyNet_error">تعذر التحقق من SafetyNet، لا يوجد إنترنت؟</string>
<string name="safetyNet_pass">SafetyNet تخطى</string>
<string name="safetyNet_fail">فشل SafetyNet: عدم تطابق التشكيل الجانبي CTS</string>
<string name="status">الحالة</string>
</resources>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Standard"</item>
<item>"Dunkel"</item>
</string-array>
</resources>

View File

@@ -3,7 +3,7 @@
<!--Welcome Activity-->
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
<string name="navigation_drawer_close">Navigationsleiste schliessen</string>
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
<string name="modules">Module</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
@@ -49,7 +49,7 @@
<string name="app_developers">Entwickler</string>
<string name="app_developers_"><![CDATA[Anwendung entwickelt von <a href="https://github.com/topjohnwu">topjohnwu</a> in Zusammenarbeit mit <a href="https://github.com/d8ahazard">Digitalhigh</a> und <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Anwendungs changelog</string>
<string name="translators" />
<string name="translators">skalnet</string>
<string name="app_version">Anwendungs Version</string>
<string name="app_source_code">Quelltext</string>
<string name="donation">Spende</string>
@@ -80,6 +80,9 @@
<string name="settings_general_category">Allgemein</string>
<string name="settings_theme_title">Aussehen</string>
<string name="settings_theme_summary">Wähle ein Theme aus</string>
<string name="theme_default">Standard</string>
<string name="theme_dark">Dunkel</string>
<string name="settings_busybox_title">BusyBox aktivieren</string>
<string name="settings_busybox_summary">Magisks eingebautes BusyBox zum PATH hinzufügen</string>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Por defecto"</item>
<item>"Oscuro"</item>
</string-array>
</resources>

View File

@@ -51,7 +51,7 @@
<string name="app_developers">Desarroladores principales</string>
<string name="app_developers_"><![CDATA[App created by <a href="https://github.com/topjohnwu">topjohnwu</a> in collaboration with <a href="https://github.com/d8ahazard">Digitalhigh</a> and <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Cambios en la aplicación</string>
<string name="translators"/>
<string name="translators">Gawenda, netizen</string>
<string name="app_version">Versión de la aplicación</string>
<string name="app_source_code">Código fuente</string>
<string name="donation">Donar</string>
@@ -82,6 +82,8 @@
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Selecciona un tema</string>
<string name="theme_default">Por defecto</string>
<string name="theme_dark">Oscuro</string>
<string name="settings_magiskhide_title">Habilitar Magisk Hide</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de varias detecciones</string>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Default"</item>
<item>"Scuro"</item>
</string-array>
</resources>

View File

@@ -49,7 +49,7 @@
<string name="app_developers">Sviluppatori</string>
<string name="app_developers_"><![CDATA[App creata da <a href="https://github.com/topjohnwu">topjohnwu</a> in collaborazione con <a href="https://github.com/d8ahazard">Digitalhigh</a> e <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Changelog</string>
<string name="translators" />
<string name="translators">Fabb2303</string>
<string name="app_version">Versione App</string>
<string name="app_source_code">Codice sorgente</string>
<string name="donation">Donazione</string>
@@ -80,6 +80,9 @@
<string name="settings_general_category">Generali</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Scegli un tema</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Scuro</string>
<string name="settings_busybox_title">Abilita BusyBox</string>
<string name="settings_busybox_summary">Make Magisk\'s built-in BusyBox be visible in PATH</string>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Standaard"</item>
<item>"Donker"</item>
</string-array>
</resources>

View File

@@ -49,7 +49,7 @@
<string name="app_developers">Hoofddevelopers</string>
<string name="app_developers_"><![CDATA[App gemaakt door <a href="https://github.com/topjohnwu">topjohnwu</a> in samenwerking met <a href="https://github.com/d8ahazard">Digitalhigh</a> en <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">App\'s changelog</string>
<string name="translators" />
<string name="translators">NaamloosDT, Klaessen</string>
<string name="app_version">App\'s versie</string>
<string name="app_source_code">Source code</string>
<string name="donation">Donatie</string>
@@ -80,6 +80,9 @@
<string name="settings_general_category">Algemeen</string>
<string name="settings_theme_title">Thema</string>
<string name="settings_theme_summary">Selecteer een thema</string>
<string name="theme_default">Standaard</string>
<string name="theme_dark">Donker</string>
<string name="settings_busybox_title">Schakel BusyBox in</string>
<string name="settings_busybox_summary">Maakt Magisk\'s ingebouwde BusyBox zichtbaar in PATH</string>
@@ -90,7 +93,6 @@
<string name="settings_shell_logging_summary">Schakel dit in om alle shell commands en output te loggen</string>
<string name="settings_magiskhide_title">Magisk verbergen</string>
<string name="settings_magiskhide_summary">Reboot om de instellingen toe te passen</string>
<string name="zip_install_unzip_zip_msg"></string>
<!-- Strings related to Settings -->

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Padrão"</item>
<item>"Escuro"</item>
</string-array>
</resources>

View File

@@ -2,21 +2,44 @@
<!--Welcome Activity-->
<string name="navigation_drawer_open">Abrir gaveta de notificação</string>
<string name="navigation_drawer_close">Fechar gaveta de notificação</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Módulos</string>
<string name="downloads">Baixar</string>
<string name="log">Registro</string>
<string name="settings">Configurações</string>
<!--Magisk Fragment-->
<string name="status">Status</string>
<string name="install">Instalar</string>
<!--Status Fragment-->
<string name="magisk_version">Magisk v%1$s Instalado</string>
<string name="magisk_version_error">Você instalou o Magisk?</string>
<string name="magisk_version_error">Magisk não instalado</string>
<string name="magisk_update_available">Atualização para Magisk v%1$.1f!</string>
<string name="checking_for_updates">Checando por atualizações…</string>
<string name="magisk_update_available">Magisk v%1$.1f disponível!</string>
<string name="cannot_check_updates">Não é possível verificar se há atualizações</string>
<string name="up_to_date">Última versão do %1$s instalado</string>
<!--Root Fragment-->
<string name="root_error">Rooteado mas sem permissão de root, o acesso foi permitido?</string>
<string name="not_rooted">Sem root</string>
<string name="proper_root">Rooteado</string>
<string name="safetyNet_check_text">Pressione para checar o SafetyNet</string>
<string name="checking_safetyNet_status">Checando status do SafetyNet…</string>
<string name="safetyNet_connection_failed">Não é possível conectar-se à API do Google</string>
<string name="safetyNet_connection_suspended">A conexão com API do Google foi suspensa</string>
<string name="safetyNet_error">Não é possível verificar o SafetyNet, sem Internet?</string>
<string name="safetyNet_fail">SafetyNet Falhou: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Passado</string>
<string name="root_info_warning">Funcionalidade muito limitada</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Local da Boot Image</string>
<string name="detect_button">Detectar</string>
<string name="advanced_settings_title">Configurações avançadas</string>
<string name="keep_force_encryption">Keep force encryption</string>
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="current_magisk_title">Versão instalada do Magisk: v%1$s</string>
<string name="install_magisk_title">Última versão do Magisk: v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(Nenhuma informação fornecida)</string>
<string name="no_modules_found">Nenhum módulo encontrado</string>
@@ -26,6 +49,7 @@
<string name="disable_file_created">Módulo será desativado na próxima reinicialização</string>
<string name="disable_file_removed">Módulo será ativado na próxima reinicialização</string>
<string name="author">Criado por %1$s</string>
<string name="fab_flash_zip">Flashear Módulo Zip</string>
<!--Repo Fragment-->
<string name="update_available">Atualização disponível</string>
@@ -47,7 +71,7 @@
<string name="app_developers">Principais desenvolvedores</string>
<string name="app_developers_"><![CDATA[App criado por <a href="https://github.com/topjohnwu">topjohnwu</a> em colaboração com <a href="https://github.com/d8ahazard">Digitalhigh</a> e <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Registro de Mudanças do App</string>
<string name="translators" />
<string name="translators">Killer7Mod</string>
<string name="app_version">Versão do App</string>
<string name="app_source_code">Código fonte</string>
<string name="donation">Doação</string>
@@ -59,42 +83,48 @@
<string name="no_thanks">Não, Obrigado</string>
<string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">Você deseja instalar%1$s ?</string>
<string name="download_install">Baixar e instalar</string>
<string name="download_install">Baixar &amp; instalar</string>
<string name="goto_install">Ir na seção \"Instalar\"</string>
<string name="download_file_error">Erro ao baixar o arquivo</string>
<string name="install_error">Erro na instalação!</string>
<string name="manual_install_1">Erro ao flashear o arquivo, arquivo zip colocado em %1$s\nFlashear isto na recuperação manualmente</string>
<string name="manual_install_1">Erro ao flashear o arquivo, arquivo zip colocado em %1$s</string>
<string name="manual_install_2">Flashear isto na recuperação manualmente</string>
<string name="invalid_zip">O zip não é um Módulo Magisk!!</string>
<string name="reboot_title">Instalação bem-sucedida!</string>
<string name="reboot_msg">Você quer reiniciar agora?</string>
<string name="reboot">Reiniciar</string>
<string name="copying_msg">Copiando zip para diretório temporário</string>
<string name="zip_install_progress_title">Instalando</string>
<string name="zip_install_unzip_zip_msg">Descompactando arquivo zip …</string>
<string name="zip_install_process_zip_msg">Processando arquivo zip …</string>
<string name="zip_install_progress_msg">"Instalando %1$s …"</string>
<string name="no_magisk_title">Magisk Não Instalado!</string>
<string name="no_magisk_msg">Você quer baixar e instalar o Magisk?</string>
<!--URL Templates-->
<string name="downloading_toast">Baixando %1$s</string>
<string name="magisk_update_title">Nova atualização do Magisk disponível!</string>
<string name="magisk_update_message">Magisk v%1$.1f Atualização está pronta, você quer instalar?</string>
<string name="check_release_notes">Verificar as notas da atualização</string>
<!--Settings Fragment -->
<string name="settings_general_category">Geral</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Escolha um tema</string>
<string name="theme_default">Padrão</string>
<string name="theme_dark">Escuro</string>
<string name="settings_magiskhide_title">Ativar Magisk Hide</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de várias detecções</string>
<string name="settings_busybox_title">Ativar BusyBox</string>
<string name="settings_busybox_summary">Marque essa opção para o BusyBox do Magisk ser visível no PATH</string>
<string name="settings_busybox_summary">Monta a busybox interna do Magisk\'s para xbin</string>
<string name="settings_hosts_title">Ativar systemless hosts</string>
<string name="settings_hosts_summary">Suporte do systemless para Adblock apps</string>
<string name="settings_development_category">Desenvolvimento</string>
<string name="settings_developer_logging_title">Ativar registro mais detalhado</string>
<string name="settings_developer_logging_summary">Marque essa opção para habilitar um registro mais detalhado</string>
<string name="settings_shell_logging_title">Ativar registro da shell de comando</string>
<string name="settings_shell_logging_summary">Marque esta opção para habilitar o registro de todos os comandos shell e de saída</string>
<string name="settings_magiskhide_title">Ativar Magisk Hide</string>
<string name="settings_magiskhide_summary">Reiniciar para aplicar as configurações</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<!-- Example settings for Data & Sync -->
<!-- Example settings for Notifications -->
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
</resources>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"默认"</item>
<item>"深色"</item>
</string-array>
</resources>

View File

@@ -9,16 +9,34 @@
<string name="downloads">下载</string>
<string name="log">日志</string>
<string name="settings">设置</string>
<string name="status">状态</string>
<string name="install">安装</string>
<!--Magisk Fragment-->
<!--Status Fragment-->
<string name="magisk_version">已安装 Magisk v%1$s</string>
<string name="magisk_version_error">你是否已安装 Magisk</string>
<string name="checking_for_updates">正在检查更新…</string>
<string name="magisk_update_available">Magisk 可更新到 v%1$.1f</string>
<string name="cannot_check_updates">无法检查更新</string>
<string name="cannot_check_updates">无法检查更新,没有网络连接?</string>
<string name="up_to_date">已安装最新版本的 %1$s</string>
<string name="root_error">已 ROOT 但没有 ROOT 权限,未授予权限?</string>
<string name="not_rooted">未 ROOT</string>
<string name="proper_root">已正确 ROOT</string>
<string name="checking_safetyNet_status">正在检查 SafetyNet 状态…</string>
<string name="safetyNet_error">无法检查 SafetyNet没有网络连接</string>
<string name="safetyNet_fail">SafetyNet 失败CTS 配置文件不匹配</string>
<string name="safetyNet_pass">SafetyNet 已通过</string>
<string name="root_info_warning">功能严重受限</string>
<!--Root Fragment-->
<!--Install Fragment-->
<string name="auto_detect">"(自动) %1$s"</string>
<string name="boot_image_title">Boot 镜像位置</string>
<string name="detect_button">检测</string>
<string name="advanced_settings_title">高级设置</string>
<string name="keep_force_encryption">保持强制加密</string>
<string name="keep_dm_verity">保持 dm-verity</string>
<string name="install_magisk_title">安装 Magisk 版本v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(未提供信息)</string>
@@ -51,7 +69,7 @@
<string name="app_developers">主要开发者</string>
<string name="app_developers_"><![CDATA[此应用由 <a href="https://github.com/topjohnwu">topjohnwu</a> 与 <a href="https://github.com/d8ahazard">Digitalhigh</a> 和 <a href="https://github.com/dvdandroid">Dvdandroid</a> 合作开发。]]></string>
<string name="app_changelog">应用更新日志</string>
<string name="translators"/>
<string name="translators">gh2923</string>
<string name="app_version">应用版本</string>
<string name="app_source_code">源代码</string>
<string name="donation">捐赠</string>
@@ -64,6 +82,7 @@
<string name="repo_install_title">安装 %1$s</string>
<string name="repo_install_msg">你想要安装 %1$s 吗?</string>
<string name="download_install">下载并安装</string>
<string name="goto_install">前往“安装”界面</string>
<string name="download_file_error">下载文件时出错</string>
<string name="install_error">安装出错!</string>
<string name="manual_install_1">Zip 文件已保存至 %1$s</string>
@@ -72,12 +91,14 @@
<string name="reboot_title">安装成功!</string>
<string name="reboot_msg">你想要立即重启吗?</string>
<string name="reboot">重启</string>
<string name="copying_msg">正在复制 zip 到临时目录</string>
<string name="zip_install_progress_title">正在安装</string>
<string name="zip_install_unzip_zip_msg">正在解压 zip 文件 …</string>
<string name="zip_install_process_zip_msg">正在处理 zip 文件 …</string>
<string name="zip_install_progress_msg">"正在安装 %1$s …"</string>
<string name="no_magisk_title">未安装 Magisk</string>
<string name="no_magisk_msg">你想要下载并安装 Magisk 吗?</string>
<string name="downloading_toast">正在下载 %1$s</string>
<!--URL Templates-->
@@ -85,6 +106,8 @@
<string name="settings_general_category">常规</string>
<string name="settings_theme_title">主题</string>
<string name="settings_theme_summary">选择一个主题</string>
<string name="theme_default">默认</string>
<string name="theme_dark">深色</string>
<string name="settings_magiskhide_title">启用 Magisk 隐藏</string>
<string name="settings_magiskhide_summary">隐藏 Magisk 使其不被多种方法检测到</string>

View File

@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>"Default"</item>
<item>"Dark"</item>
<item>@string/theme_default</item>
<item>@string/theme_dark</item>
</string-array>
<string-array name="themes_values">
<item>@string/theme_default_value</item>
<item>@string/theme_dark_value</item>
</string-array>
</resources>

View File

@@ -25,7 +25,10 @@
<string name="root_error">Rooted but no root permission, not allowed?</string>
<string name="not_rooted">Not rooted</string>
<string name="proper_root">Properly rooted</string>
<string name="safetyNet_check_text">Tap to start SafetyNet check</string>
<string name="checking_safetyNet_status">Checking SafetyNet status…</string>
<string name="safetyNet_connection_failed">Cannot connect to Google API</string>
<string name="safetyNet_connection_suspended">Connection to Google API was suspended</string>
<string name="safetyNet_error">Cannot check SafetyNet, no Internet?</string>
<string name="safetyNet_fail">SafetyNet Failed: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Passed</string>
@@ -38,7 +41,8 @@
<string name="advanced_settings_title">Advanced Settings</string>
<string name="keep_force_encryption">Keep force encryption</string>
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="install_magisk_title">Install Magisk Version: v%1$.1f</string>
<string name="current_magisk_title">Installed Magisk Version: v%1$s</string>
<string name="install_magisk_title">Latest Magisk Version: v%1$.1f</string>
<string name="magiskify" translatable="false">Magiskify</string>
<!--Module Fragment-->
@@ -102,9 +106,14 @@
<string name="no_magisk_title">No Magisk Installed!</string>
<string name="no_magisk_msg">Do you want to download and install Magisk?</string>
<string name="downloading_toast">Downloading %1$s</string>
<string name="magisk_update_title">New Magisk Update Available!</string>
<string name="magisk_update_message">Magisk v%1$.1f update is live, do you want to install?</string>
<string name="settings_reboot_toast">Reboot to apply settings</string>
<string name="check_release_notes">Check release notes</string>
<string name="repo_cache_cleared">Repo cache cleared</string>
<!--URL Templates-->
<string name="url_main" translatable="false">https://api.github.com/orgs/Magisk-Modules-Repo/repos?access_token=%1$s</string>
<string name="url_main" translatable="false">https://api.github.com/orgs/Magisk-Modules-Repo/repos</string>
<string name="file_url" translatable="false">https://raw.githubusercontent.com/Magisk-Modules-Repo/%1$s/master/%2$s</string>
<string name="zip_url" translatable="false">https://github.com/Magisk-Modules-Repo/%1$s/archive/master.zip</string>
@@ -112,6 +121,10 @@
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Theme</string>
<string name="settings_theme_summary">Select a theme</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Dark</string>
<string name="settings_clear_cache_title">Clear Repo Cache</string>
<string name="settings_clear_cache_summary">Clear the cached information for online repos, forces the app to refresh online</string>
<string name="settings_magiskhide_title">Enable Magisk Hide</string>
<string name="settings_magiskhide_summary">Hide Magisk from various detections</string>
@@ -122,10 +135,13 @@
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Enable advanced debug logging</string>
<string name="settings_developer_logging_summary">Check this to enable more verbose logging.</string>
<string name="settings_developer_logging_summary">Check this to enable verbose logging</string>
<string name="settings_shell_logging_title">Enable shell command debug logging</string>
<string name="settings_shell_logging_summary">Check this to enable logging all shell commands and output</string>
<string name="settings_shell_logging_summary">Check this to enable logging all shell commands and its output</string>
<!-- Themes -->
<string name="theme_default_value" translatable="false">default</string>
<string name="theme_dark_value" translatable="false">dark</string>
<string name="settings_reboot_toast">Reboot to apply settings</string>
</resources>

View File

@@ -8,9 +8,14 @@
android:key="theme"
android:title="@string/settings_theme_title"
android:summary="@string/settings_theme_summary"
android:defaultValue="Default"
android:defaultValue="@string/theme_default_value"
android:entries="@array/themes"
android:entryValues="@array/themes"/>
android:entryValues="@array/themes_values"/>
<Preference
android:key="clear"
android:title="@string/settings_clear_cache_title"
android:summary="@string/settings_clear_cache_summary" />
</PreferenceCategory>