mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 11:37:36 +00:00
Compare commits
36 Commits
manager-v3
...
manager-v3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e1aabd70e8 | ||
![]() |
a9dc1b32e0 | ||
![]() |
01d847ae4e | ||
![]() |
61e2c3444a | ||
![]() |
5363b0f810 | ||
![]() |
f0e1a8823e | ||
![]() |
7be5937aa0 | ||
![]() |
8f43055b0e | ||
![]() |
953a81b299 | ||
![]() |
1d34ae7934 | ||
![]() |
2cabb2666b | ||
![]() |
0b59bb1a29 | ||
![]() |
c1e7d74b96 | ||
![]() |
cc262d6595 | ||
![]() |
61d43b118b | ||
![]() |
989d8181dd | ||
![]() |
cffc157d98 | ||
![]() |
2a70619577 | ||
![]() |
b91919bffa | ||
![]() |
fb7a4bf880 | ||
![]() |
4b41799a90 | ||
![]() |
123f39a21b | ||
![]() |
cadab12737 | ||
![]() |
742055c43b | ||
![]() |
fa73b41fa7 | ||
![]() |
a474eafe84 | ||
![]() |
442fcf921c | ||
![]() |
fb0923f3ab | ||
![]() |
5bb943f845 | ||
![]() |
a3109953d0 | ||
![]() |
ff266c8c79 | ||
![]() |
ef2e02098d | ||
![]() |
93598d3a51 | ||
![]() |
53aebcfb1e | ||
![]() |
bb2467d2ac | ||
![]() |
05c063b61d |
@@ -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
|
||||
|
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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"))
|
||||
|
@@ -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 {
|
||||
|
@@ -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");
|
||||
|
@@ -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() {
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
|
9
app/src/main/res/drawable/ic_safetynet.xml
Normal file
9
app/src/main/res/drawable/ic_safetynet.xml
Normal 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>
|
@@ -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>
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="themes">
|
||||
<item>"إفتراضي"</item>
|
||||
<item>"غامق"</item>
|
||||
</string-array>
|
||||
</resources>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
||||
|
@@ -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>
|
@@ -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 -->
|
||||
|
||||
|
@@ -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>
|
@@ -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 & 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>
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="themes">
|
||||
<item>"默认"</item>
|
||||
<item>"深色"</item>
|
||||
</string-array>
|
||||
</resources>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
||||
|
Reference in New Issue
Block a user