mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01: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); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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> | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user