You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Updated locations of nearly all files
This has been done in preparations for rewrite to kotlin and upcoming design changes. Nothing should be broken but use caution.
This commit is contained in:
committed by
John Wu
parent
540000d26e
commit
a028cd5cec
@@ -0,0 +1,193 @@
|
||||
package com.topjohnwu.magisk.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.ui.hide.MagiskHideFragment;
|
||||
import com.topjohnwu.magisk.ui.home.MagiskFragment;
|
||||
import com.topjohnwu.magisk.ui.log.LogFragment;
|
||||
import com.topjohnwu.magisk.ui.module.ModulesFragment;
|
||||
import com.topjohnwu.magisk.ui.module.ReposFragment;
|
||||
import com.topjohnwu.magisk.ui.settings.SettingsFragment;
|
||||
import com.topjohnwu.magisk.ui.superuser.SuperuserFragment;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class MainActivity extends BaseActivity
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private final Handler mDrawerHandler = new Handler();
|
||||
private int mDrawerItem;
|
||||
private static boolean fromShortcut = false;
|
||||
|
||||
@BindView(R.id.toolbar) public Toolbar toolbar;
|
||||
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
||||
@BindView(R.id.nav_view) NavigationView navigationView;
|
||||
|
||||
private float toolbarElevation;
|
||||
|
||||
@Override
|
||||
public int getDarkTheme() {
|
||||
return R.style.AppTheme_Dark;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
if (!SplashActivity.DONE) {
|
||||
startActivity(new Intent(this, ClassMap.get(SplashActivity.class)));
|
||||
finish();
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
new MainActivity_ViewBinding(this);
|
||||
checkHideSection();
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.magisk, R.string.magisk) {
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
super.onDrawerOpened(drawerView);
|
||||
super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed tate
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||
super.onDrawerSlide(drawerView, 0); // this disables the animation
|
||||
}
|
||||
};
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
toolbarElevation = toolbar.getElevation();
|
||||
}
|
||||
|
||||
drawer.addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
String section = getIntent().getStringExtra(Const.Key.OPEN_SECTION);
|
||||
fromShortcut = section != null;
|
||||
navigate(section);
|
||||
}
|
||||
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (drawer.isDrawerOpen(navigationView)) {
|
||||
drawer.closeDrawer(navigationView);
|
||||
} else if (mDrawerItem != R.id.magisk && !fromShortcut) {
|
||||
navigate(R.id.magisk);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) {
|
||||
mDrawerHandler.removeCallbacksAndMessages(null);
|
||||
mDrawerHandler.postDelayed(() -> navigate(menuItem.getItemId()), 250);
|
||||
drawer.closeDrawer(navigationView);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void checkHideSection() {
|
||||
Menu menu = navigationView.getMenu();
|
||||
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
|
||||
(boolean) Config.get(Config.Key.MAGISKHIDE));
|
||||
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Config.magiskVersionCode >= 0);
|
||||
menu.findItem(R.id.downloads).setVisible(Networking.checkNetworkStatus(this)
|
||||
&& Shell.rootAccess() && Config.magiskVersionCode >= 0);
|
||||
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
|
||||
menu.findItem(R.id.superuser).setVisible(Utils.showSuperUser());
|
||||
}
|
||||
|
||||
public void navigate(String item) {
|
||||
int itemId = R.id.magisk;
|
||||
if (item != null) {
|
||||
switch (item) {
|
||||
case "superuser":
|
||||
itemId = R.id.superuser;
|
||||
break;
|
||||
case "modules":
|
||||
itemId = R.id.modules;
|
||||
break;
|
||||
case "downloads":
|
||||
itemId = R.id.downloads;
|
||||
break;
|
||||
case "magiskhide":
|
||||
itemId = R.id.magiskhide;
|
||||
break;
|
||||
case "log":
|
||||
itemId = R.id.log;
|
||||
break;
|
||||
case "settings":
|
||||
itemId = R.id.settings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
navigate(itemId);
|
||||
}
|
||||
|
||||
public void navigate(int itemId) {
|
||||
mDrawerItem = itemId;
|
||||
navigationView.setCheckedItem(itemId);
|
||||
switch (itemId) {
|
||||
case R.id.magisk:
|
||||
fromShortcut = false;
|
||||
displayFragment(new MagiskFragment(), true);
|
||||
break;
|
||||
case R.id.superuser:
|
||||
displayFragment(new SuperuserFragment(), true);
|
||||
break;
|
||||
case R.id.modules:
|
||||
displayFragment(new ModulesFragment(), true);
|
||||
break;
|
||||
case R.id.downloads:
|
||||
displayFragment(new ReposFragment(), true);
|
||||
break;
|
||||
case R.id.magiskhide:
|
||||
displayFragment(new MagiskHideFragment(), true);
|
||||
break;
|
||||
case R.id.log:
|
||||
displayFragment(new LogFragment(), false);
|
||||
break;
|
||||
case R.id.settings:
|
||||
displayFragment(new SettingsFragment(), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void displayFragment(@NonNull Fragment navFragment, boolean setElevation) {
|
||||
supportInvalidateOptionsMenu();
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.replace(R.id.content_frame, navFragment)
|
||||
.commitNow();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
toolbar.setElevation(setElevation ? toolbarElevation : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.topjohnwu.magisk.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.topjohnwu.magisk.BuildConfig;
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper;
|
||||
import com.topjohnwu.magisk.tasks.CheckUpdates;
|
||||
import com.topjohnwu.magisk.tasks.UpdateRepos;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.Notifications;
|
||||
import com.topjohnwu.magisk.view.Shortcuts;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
public class SplashActivity extends BaseActivity {
|
||||
|
||||
public static boolean DONE = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Shell.getShell(shell -> {
|
||||
if (Config.magiskVersionCode > 0 &&
|
||||
Config.magiskVersionCode < Const.MAGISK_VER.MIN_SUPPORT) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.unsupport_magisk_title)
|
||||
.setMessage(R.string.unsupport_magisk_message)
|
||||
.setNegativeButton(R.string.ok, null)
|
||||
.setOnDismissListener(dialog -> finish())
|
||||
.show();
|
||||
} else {
|
||||
initAndStart();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initAndStart() {
|
||||
String pkg = Config.get(Config.Key.SU_MANAGER);
|
||||
if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||
Config.remove(Config.Key.SU_MANAGER);
|
||||
Shell.su("pm uninstall " + pkg).submit();
|
||||
}
|
||||
if (TextUtils.equals(pkg, getPackageName())) {
|
||||
try {
|
||||
// We are the manager, remove com.topjohnwu.magisk as it could be malware
|
||||
getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0);
|
||||
Shell.su("pm uninstall " + BuildConfig.APPLICATION_ID).submit();
|
||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||
}
|
||||
|
||||
// Dynamic detect all locales
|
||||
LocaleManager.loadAvailableLocales(R.string.app_changelog);
|
||||
|
||||
// Set default configs
|
||||
Config.initialize();
|
||||
|
||||
// Create notification channel on Android O
|
||||
Notifications.setup(this);
|
||||
|
||||
// Schedule periodic update checks
|
||||
Utils.scheduleUpdateCheck();
|
||||
CheckUpdates.check();
|
||||
|
||||
// Setup shortcuts
|
||||
Shortcuts.setup(this);
|
||||
|
||||
// Create repo database
|
||||
app.repoDB = new RepoDatabaseHelper(this);
|
||||
|
||||
// Magisk working as expected
|
||||
if (Shell.rootAccess() && Config.magiskVersionCode > 0) {
|
||||
// Load modules
|
||||
Utils.loadModules(false);
|
||||
// Load repos
|
||||
if (Networking.checkNetworkStatus(this))
|
||||
new UpdateRepos().exec();
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, ClassMap.get(MainActivity.class));
|
||||
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
||||
DONE = true;
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.topjohnwu.magisk.ui.base;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity implements Event.AutoListener {
|
||||
|
||||
static int[] EMPTY_INT_ARRAY = new int[0];
|
||||
|
||||
private SparseArrayCompat<ActivityResultListener> resultListeners = new SparseArrayCompat<>();
|
||||
public App app = App.self;
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return EMPTY_INT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {}
|
||||
|
||||
@StyleRes
|
||||
public int getDarkTheme() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(LocaleManager.getLocaleContext(base, LocaleManager.locale));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Event.register(this);
|
||||
if (getDarkTheme() != -1 && (boolean) Config.get(Config.Key.DARK_THEME)) {
|
||||
setTheme(getDarkTheme());
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Event.unregister(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
protected void setFloating() {
|
||||
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
|
||||
if (isTablet) {
|
||||
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
|
||||
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
|
||||
params.alpha = 1.0f;
|
||||
params.dimAmount = 0.6f;
|
||||
params.flags |= 2;
|
||||
getWindow().setAttributes(params);
|
||||
setFinishOnTouchOutside(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void lockOrientation() {
|
||||
if (Build.VERSION.SDK_INT < 18)
|
||||
setRequestedOrientation(getResources().getConfiguration().orientation);
|
||||
else
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
||||
}
|
||||
|
||||
public void runWithExternalRW(Runnable callback) {
|
||||
runWithPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, callback);
|
||||
}
|
||||
|
||||
public void runWithPermissions(String[] permissions, Runnable callback) {
|
||||
runWithPermissions(this, permissions, callback);
|
||||
}
|
||||
|
||||
public static void runWithPermissions(Context context, String[] permissions, Runnable callback) {
|
||||
boolean granted = true;
|
||||
for (String perm : permissions) {
|
||||
if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED)
|
||||
granted = false;
|
||||
}
|
||||
if (granted) {
|
||||
Const.EXTERNAL_PATH.mkdirs();
|
||||
callback.run();
|
||||
} else {
|
||||
// Passed in context should be an activity if not granted, need to show dialog!
|
||||
if (context instanceof BaseActivity) {
|
||||
BaseActivity activity = (BaseActivity) context;
|
||||
int code = callback.hashCode() & 0xFFFF;
|
||||
activity.resultListeners.put(code, ((i, d) -> callback.run()));
|
||||
ActivityCompat.requestPermissions(activity, permissions, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
onActivityResultListener(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private void onActivityResultListener(int requestCode, int resultCode, Intent data) {
|
||||
ActivityResultListener listener = resultListeners.get(requestCode);
|
||||
if (listener != null) {
|
||||
resultListeners.remove(requestCode);
|
||||
listener.onActivityResult(resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void startActivityForResult(Intent intent, int requestCode, ActivityResultListener listener) {
|
||||
resultListeners.put(requestCode, listener);
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
boolean grant = true;
|
||||
for (int result : grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED)
|
||||
grant = false;
|
||||
}
|
||||
if (grant)
|
||||
onActivityResultListener(requestCode, 0, null);
|
||||
else
|
||||
resultListeners.remove(requestCode);
|
||||
}
|
||||
|
||||
public interface ActivityResultListener {
|
||||
void onActivityResult(int resultCode, Intent data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferences getSharedPreferences(String name, int mode) {
|
||||
if (TextUtils.equals(name, getPackageName() + "_preferences"))
|
||||
return app.prefs;
|
||||
return super.getSharedPreferences(name, mode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.topjohnwu.magisk.ui.base;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public abstract class BaseFragment extends Fragment implements Event.AutoListener {
|
||||
|
||||
public App app = App.self;
|
||||
protected Unbinder unbinder = null;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Event.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Event.unregister(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (unbinder != null)
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode) {
|
||||
startActivityForResult(intent, requestCode, (resultCode, data) ->
|
||||
onActivityResult(requestCode, resultCode, data));
|
||||
}
|
||||
|
||||
public void startActivityForResult(Intent intent, int requestCode,
|
||||
BaseActivity.ActivityResultListener listener) {
|
||||
((BaseActivity) requireActivity()).startActivityForResult(intent, requestCode, listener);
|
||||
}
|
||||
|
||||
protected void runWithExternalRW(Runnable callback) {
|
||||
((BaseActivity) requireActivity()).runWithExternalRW(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return BaseActivity.EMPTY_INT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.topjohnwu.magisk.ui.base;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceGroupAdapter;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Event.AutoListener {
|
||||
|
||||
public App app = App.self;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
app.prefs.registerOnSharedPreferenceChangeListener(this);
|
||||
Event.register(this);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
app.prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
Event.unregister(this);
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return BaseActivity.EMPTY_INT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
|
||||
return new PreferenceGroupAdapter(preferenceScreen) {
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
|
||||
super.onBindViewHolder(holder, position);
|
||||
Preference preference = getItem(position);
|
||||
if (preference instanceof PreferenceCategory)
|
||||
setZeroPaddingToLayoutChildren(holder.itemView);
|
||||
else {
|
||||
View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
|
||||
if (iconFrame != null) {
|
||||
iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setZeroPaddingToLayoutChildren(View view) {
|
||||
if (!(view instanceof ViewGroup))
|
||||
return;
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
int childCount = viewGroup.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
setZeroPaddingToLayoutChildren(viewGroup.getChildAt(i));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
viewGroup.setPaddingRelative(0, viewGroup.getPaddingTop(), viewGroup.getPaddingEnd(), viewGroup.getPaddingBottom());
|
||||
else
|
||||
viewGroup.setPadding(0, viewGroup.getPaddingTop(), viewGroup.getPaddingRight(), viewGroup.getPaddingBottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package com.topjohnwu.magisk.ui.flash;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.StringListAdapter;
|
||||
import com.topjohnwu.magisk.tasks.FlashZip;
|
||||
import com.topjohnwu.magisk.tasks.MagiskInstaller;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.utils.RootUtils;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.superuser.CallbackList;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindColor;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class FlashActivity extends BaseActivity {
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
@BindView(R.id.button_panel) LinearLayout buttonPanel;
|
||||
@BindView(R.id.reboot) Button reboot;
|
||||
@BindView(R.id.recyclerView) RecyclerView rv;
|
||||
@BindColor(android.R.color.white) int white;
|
||||
|
||||
private List<String> console, logs;
|
||||
|
||||
@OnClick(R.id.reboot)
|
||||
void reboot() {
|
||||
RootUtils.reboot();
|
||||
}
|
||||
|
||||
@OnClick(R.id.save_logs)
|
||||
void saveLogs() {
|
||||
runWithExternalRW(() -> {
|
||||
Calendar now = Calendar.getInstance();
|
||||
String filename = String.format(Locale.US,
|
||||
"magisk_install_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||
|
||||
File logFile = new File(Const.EXTERNAL_PATH, filename);
|
||||
try (FileWriter writer = new FileWriter(logFile)) {
|
||||
for (String s : logs) {
|
||||
writer.write(s);
|
||||
writer.write('\n');
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
Utils.toast(logFile.getPath(), Toast.LENGTH_LONG);
|
||||
});
|
||||
}
|
||||
|
||||
@OnClick(R.id.close)
|
||||
public void close() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Prevent user accidentally press back button
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDarkTheme() {
|
||||
return R.style.AppTheme_NoDrawer_Dark;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_flash);
|
||||
new FlashActivity_ViewBinding(this);
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setTitle(R.string.flashing);
|
||||
}
|
||||
setFloating();
|
||||
setFinishOnTouchOutside(false);
|
||||
if (!Shell.rootAccess())
|
||||
reboot.setVisibility(View.GONE);
|
||||
|
||||
logs = Collections.synchronizedList(new ArrayList<>());
|
||||
console = new ConsoleList();
|
||||
rv.setAdapter(new ConsoleAdapter());
|
||||
|
||||
Intent intent = getIntent();
|
||||
Uri uri = intent.getData();
|
||||
|
||||
switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
|
||||
case Const.Value.FLASH_ZIP:
|
||||
new FlashModule(uri).exec();
|
||||
break;
|
||||
case Const.Value.UNINSTALL:
|
||||
new Uninstall(uri).exec();
|
||||
break;
|
||||
case Const.Value.FLASH_MAGISK:
|
||||
new DirectInstall().exec();
|
||||
break;
|
||||
case Const.Value.FLASH_INACTIVE_SLOT:
|
||||
new SecondSlot().exec();
|
||||
break;
|
||||
case Const.Value.PATCH_FILE:
|
||||
new PatchFile(uri).exec();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private class ConsoleAdapter extends StringListAdapter<ConsoleAdapter.ViewHolder> {
|
||||
|
||||
ConsoleAdapter() {
|
||||
super(console, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int itemLayoutRes() {
|
||||
return R.layout.list_item_console;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder createViewHolder(@NonNull View v) {
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
class ViewHolder extends StringListAdapter.ViewHolder {
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
txt.setTextColor(white);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int textViewResId() {
|
||||
return R.id.txt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ConsoleList extends CallbackList<String> {
|
||||
|
||||
ConsoleList() {
|
||||
super(new ArrayList<>());
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
rv.getAdapter().notifyItemChanged(size() - 1);
|
||||
rv.postDelayed(() -> rv.smoothScrollToPosition(size() - 1), 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddElement(String s) {
|
||||
logs.add(s);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String set(int i, String s) {
|
||||
String ret = super.set(i, s);
|
||||
UiThreadHandler.run(this::updateUI);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private class FlashModule extends FlashZip {
|
||||
|
||||
FlashModule(Uri uri) {
|
||||
super(uri, console, logs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResult(boolean success) {
|
||||
if (success) {
|
||||
Utils.loadModules();
|
||||
} else {
|
||||
console.add("! Installation failed");
|
||||
reboot.setVisibility(View.GONE);
|
||||
}
|
||||
buttonPanel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private class Uninstall extends FlashModule {
|
||||
|
||||
Uninstall(Uri uri) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResult(boolean success) {
|
||||
if (success)
|
||||
UiThreadHandler.handler.postDelayed(Shell.su("pm uninstall " + getPackageName())::exec, 3000);
|
||||
else
|
||||
super.onResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class BaseInstaller extends MagiskInstaller {
|
||||
BaseInstaller() {
|
||||
super(console, logs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResult(boolean success) {
|
||||
if (success) {
|
||||
console.add("- All done!");
|
||||
} else {
|
||||
Shell.sh("rm -rf " + installDir).submit();
|
||||
console.add("! Installation failed");
|
||||
reboot.setVisibility(View.GONE);
|
||||
}
|
||||
buttonPanel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectInstall extends BaseInstaller {
|
||||
|
||||
@Override
|
||||
protected boolean operations() {
|
||||
return findImage() && extractZip() && patchBoot() && flashBoot();
|
||||
}
|
||||
}
|
||||
|
||||
private class SecondSlot extends BaseInstaller {
|
||||
|
||||
@Override
|
||||
protected boolean operations() {
|
||||
return findSecondaryImage() && extractZip() && patchBoot() && flashBoot() && postOTA();
|
||||
}
|
||||
}
|
||||
|
||||
private class PatchFile extends BaseInstaller {
|
||||
|
||||
private Uri uri;
|
||||
|
||||
PatchFile(Uri u) {
|
||||
uri = u;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean operations() {
|
||||
return extractZip() && handleFile(uri) && patchBoot() && storeBoot();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.topjohnwu.magisk.ui.hide;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.ApplicationAdapter;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class MagiskHideFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
|
||||
private SearchView search;
|
||||
private ApplicationAdapter adapter;
|
||||
private SearchView.OnQueryTextListener searchListener;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
||||
unbinder = new MagiskHideFragment_ViewBinding(this, view);
|
||||
|
||||
adapter = new ApplicationAdapter(requireActivity());
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(adapter::refresh);
|
||||
|
||||
searchListener = new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
adapter.filter(query);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
adapter.filter(newText);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
requireActivity().setTitle(R.string.magiskhide);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_magiskhide, menu);
|
||||
search = (SearchView) menu.findItem(R.id.app_search).getActionView();
|
||||
search.setOnQueryTextListener(searchListener);
|
||||
menu.findItem(R.id.show_system).setChecked(Config.get(Config.Key.SHOW_SYSTEM_APP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.show_system) {
|
||||
boolean showSystem = !item.isChecked();
|
||||
item.setChecked(showSystem);
|
||||
Config.set(Config.Key.SHOW_SYSTEM_APP, showSystem);
|
||||
adapter.setShowSystem(showSystem);
|
||||
adapter.filter(search.getQuery().toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.MAGISK_HIDE_DONE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
adapter.filter(search.getQuery().toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
package com.topjohnwu.magisk.ui.home;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.topjohnwu.magisk.BuildConfig;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.tasks.CheckUpdates;
|
||||
import com.topjohnwu.magisk.ui.MainActivity;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.ArrowExpandable;
|
||||
import com.topjohnwu.magisk.view.Expandable;
|
||||
import com.topjohnwu.magisk.view.ExpandableViewHolder;
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow;
|
||||
import com.topjohnwu.magisk.view.SafetyNet;
|
||||
import com.topjohnwu.magisk.view.UpdateCardHolder;
|
||||
import com.topjohnwu.magisk.view.dialogs.EnvFixDialog;
|
||||
import com.topjohnwu.magisk.view.dialogs.MagiskInstallDialog;
|
||||
import com.topjohnwu.magisk.view.dialogs.ManagerInstallDialog;
|
||||
import com.topjohnwu.magisk.view.dialogs.UninstallDialog;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.Fade;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionManager;
|
||||
import androidx.transition.TransitionSet;
|
||||
import butterknife.BindColor;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class MagiskFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
private static boolean shownDialog = false;
|
||||
|
||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
@BindView(R.id.linearLayout) LinearLayout root;
|
||||
|
||||
@BindView(R.id.install_option_card) CardView installOptionCard;
|
||||
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
|
||||
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
|
||||
@BindView(R.id.install_option_expand) ViewGroup optionExpandLayout;
|
||||
@BindView(R.id.arrow) ImageView arrow;
|
||||
|
||||
@BindView(R.id.uninstall_button) CardView uninstallButton;
|
||||
|
||||
@BindColor(R.color.red500) int colorBad;
|
||||
@BindColor(R.color.green500) int colorOK;
|
||||
@BindColor(R.color.yellow500) int colorWarn;
|
||||
@BindColor(R.color.green500) int colorNeutral;
|
||||
@BindColor(R.color.blue500) int colorInfo;
|
||||
|
||||
private UpdateCardHolder magisk;
|
||||
private UpdateCardHolder manager;
|
||||
private SafetyNet safetyNet;
|
||||
private Transition transition;
|
||||
private Expandable optionExpand;
|
||||
|
||||
private void magiskInstall(View v) {
|
||||
// Show Manager update first
|
||||
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||
new ManagerInstallDialog(requireActivity()).show();
|
||||
return;
|
||||
}
|
||||
new MagiskInstallDialog((BaseActivity) requireActivity()).show();
|
||||
}
|
||||
|
||||
private void managerInstall(View v) {
|
||||
new ManagerInstallDialog(requireActivity()).show();
|
||||
}
|
||||
|
||||
private void openLink(String url) {
|
||||
Utils.openLink(requireActivity(), Uri.parse(url));
|
||||
}
|
||||
|
||||
@OnClick(R.id.paypal)
|
||||
void paypal() {
|
||||
openLink(Const.Url.PAYPAL_URL);
|
||||
}
|
||||
|
||||
@OnClick(R.id.patreon)
|
||||
void patreon() {
|
||||
openLink(Const.Url.PATREON_URL);
|
||||
}
|
||||
|
||||
@OnClick(R.id.twitter)
|
||||
void twitter() {
|
||||
openLink(Const.Url.TWITTER_URL);
|
||||
}
|
||||
|
||||
@OnClick(R.id.github)
|
||||
void github() {
|
||||
openLink(Const.Url.SOURCE_CODE_URL);
|
||||
}
|
||||
|
||||
@OnClick(R.id.xda)
|
||||
void xda() {
|
||||
openLink(Const.Url.XDA_THREAD);
|
||||
}
|
||||
|
||||
@OnClick(R.id.uninstall_button)
|
||||
void uninstall() {
|
||||
new UninstallDialog(requireActivity()).show();
|
||||
}
|
||||
|
||||
@OnClick(R.id.arrow)
|
||||
void expandOptions() {
|
||||
if (optionExpand.isExpanded())
|
||||
optionExpand.collapse();
|
||||
else optionExpand.expand();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
|
||||
unbinder = new MagiskFragment_ViewBinding(this, v);
|
||||
requireActivity().setTitle(R.string.magisk);
|
||||
|
||||
optionExpand = new ArrowExpandable(new ExpandableViewHolder(optionExpandLayout), arrow);
|
||||
safetyNet = new SafetyNet(v);
|
||||
magisk = new UpdateCardHolder(inflater, root);
|
||||
manager = new UpdateCardHolder(inflater, root);
|
||||
manager.setClickable(vv ->
|
||||
MarkDownWindow.show(requireActivity(), null,
|
||||
getResources().openRawResource(R.raw.changelog)));
|
||||
root.addView(magisk.itemView, 1);
|
||||
root.addView(manager.itemView, 2);
|
||||
|
||||
keepVerityChkbox.setChecked(Config.keepVerity);
|
||||
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> Config.keepVerity = checked);
|
||||
keepEncChkbox.setChecked(Config.keepEnc);
|
||||
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> Config.keepEnc = checked);
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
||||
|
||||
magisk.install.setOnClickListener(this::magiskInstall);
|
||||
manager.install.setOnClickListener(this::managerInstall);
|
||||
if (Config.get(Config.Key.COREONLY)) {
|
||||
magisk.additional.setText(R.string.core_only_enabled);
|
||||
magisk.additional.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (!app.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||
manager.additional.setText("(" + app.getPackageName() + ")");
|
||||
manager.additional.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
transition = new TransitionSet()
|
||||
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.addTransition(new Fade(Fade.OUT))
|
||||
.addTransition(new ChangeBounds())
|
||||
.addTransition(new Fade(Fade.IN));
|
||||
|
||||
updateUI();
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
safetyNet.unbinder.unbind();
|
||||
magisk.unbinder.unbind();
|
||||
manager.unbinder.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
TransitionManager.beginDelayedTransition(root, transition);
|
||||
safetyNet.reset();
|
||||
magisk.reset();
|
||||
manager.reset();
|
||||
|
||||
Config.loadMagiskInfo();
|
||||
updateUI();
|
||||
|
||||
Event.reset(this);
|
||||
Config.remoteMagiskVersionString = null;
|
||||
Config.remoteMagiskVersionCode = -1;
|
||||
|
||||
shownDialog = false;
|
||||
|
||||
// Trigger state check
|
||||
if (Networking.checkNetworkStatus(app)) {
|
||||
CheckUpdates.check();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.UPDATE_CHECK_DONE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
updateCheckUI();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
((MainActivity) requireActivity()).checkHideSection();
|
||||
int image, color;
|
||||
String status;
|
||||
if (Config.magiskVersionCode < 0) {
|
||||
color = colorBad;
|
||||
image = R.drawable.ic_cancel;
|
||||
status = getString(R.string.magisk_version_error);
|
||||
magisk.status.setText(status);
|
||||
magisk.currentVersion.setVisibility(View.GONE);
|
||||
} else {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
status = getString(R.string.magisk);
|
||||
magisk.currentVersion.setText(getString(R.string.current_installed,
|
||||
String.format(Locale.US, "v%s (%d)",
|
||||
Config.magiskVersionString, Config.magiskVersionCode)));
|
||||
}
|
||||
magisk.statusIcon.setColorFilter(color);
|
||||
magisk.statusIcon.setImageResource(image);
|
||||
|
||||
manager.statusIcon.setColorFilter(colorOK);
|
||||
manager.statusIcon.setImageResource(R.drawable.ic_check_circle);
|
||||
manager.currentVersion.setText(getString(R.string.current_installed,
|
||||
String.format(Locale.US, "v%s (%d)",
|
||||
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||
|
||||
if (!Networking.checkNetworkStatus(app)) {
|
||||
// No network, updateCheckUI will not be triggered
|
||||
magisk.status.setText(status);
|
||||
manager.status.setText(R.string.app_name);
|
||||
magisk.setValid(false);
|
||||
manager.setValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCheckUI() {
|
||||
int image, color;
|
||||
String status, button = "";
|
||||
|
||||
TransitionManager.beginDelayedTransition(root, transition);
|
||||
|
||||
if (Config.remoteMagiskVersionCode < 0) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
status = getString(R.string.invalid_update_channel);
|
||||
} else {
|
||||
magisk.latestVersion.setText(getString(R.string.latest_version,
|
||||
String.format(Locale.US, "v%s (%d)",
|
||||
Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode)));
|
||||
if (Config.remoteMagiskVersionCode > Config.magiskVersionCode) {
|
||||
color = colorInfo;
|
||||
image = R.drawable.ic_update;
|
||||
status = getString(R.string.magisk_update_title);
|
||||
button = getString(R.string.update);
|
||||
} else {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
status = getString(R.string.magisk_up_to_date);
|
||||
button = getString(R.string.install);
|
||||
}
|
||||
}
|
||||
if (Config.magiskVersionCode > 0) {
|
||||
// Only override status if Magisk is installed
|
||||
magisk.statusIcon.setImageResource(image);
|
||||
magisk.statusIcon.setColorFilter(color);
|
||||
magisk.status.setText(status);
|
||||
magisk.install.setText(button);
|
||||
}
|
||||
|
||||
if (Config.remoteManagerVersionCode < 0) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
status = getString(R.string.invalid_update_channel);
|
||||
} else {
|
||||
manager.latestVersion.setText(getString(R.string.latest_version,
|
||||
String.format(Locale.US, "v%s (%d)",
|
||||
Config.remoteManagerVersionString, Config.remoteManagerVersionCode)));
|
||||
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||
color = colorInfo;
|
||||
image = R.drawable.ic_update;
|
||||
status = getString(R.string.manager_update_title);
|
||||
manager.install.setText(R.string.update);
|
||||
} else {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
status = getString(R.string.manager_up_to_date);
|
||||
manager.install.setText(R.string.install);
|
||||
}
|
||||
}
|
||||
manager.statusIcon.setImageResource(image);
|
||||
manager.statusIcon.setColorFilter(color);
|
||||
manager.status.setText(status);
|
||||
|
||||
magisk.setValid(Config.remoteMagiskVersionCode > 0);
|
||||
manager.setValid(Config.remoteManagerVersionCode > 0);
|
||||
|
||||
if (Config.remoteMagiskVersionCode < 0) {
|
||||
// Hide install related components
|
||||
installOptionCard.setVisibility(View.GONE);
|
||||
uninstallButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
// Show install related components
|
||||
installOptionCard.setVisibility(View.VISIBLE);
|
||||
uninstallButton.setVisibility(Shell.rootAccess() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
if (!shownDialog && Config.magiskVersionCode > 0 &&
|
||||
!Shell.su("env_check").exec().isSuccess()) {
|
||||
shownDialog = true;
|
||||
new EnvFixDialog(requireActivity()).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.topjohnwu.magisk.ui.log;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.TabFragmentAdapter;
|
||||
import com.topjohnwu.magisk.ui.MainActivity;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class LogFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.container) ViewPager viewPager;
|
||||
@BindView(R.id.tab) TabLayout tab;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View v = inflater.inflate(R.layout.fragment_log, container, false);
|
||||
unbinder = new LogFragment_ViewBinding(this, v);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
((MainActivity) requireActivity()).toolbar.setElevation(0);
|
||||
}
|
||||
|
||||
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
|
||||
|
||||
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
|
||||
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
|
||||
tab.setupWithViewPager(viewPager);
|
||||
tab.setVisibility(View.VISIBLE);
|
||||
|
||||
viewPager.setAdapter(adapter);
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.topjohnwu.magisk.ui.log;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.StringListAdapter;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.SnackbarMaker;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
import com.topjohnwu.superuser.internal.NOPList;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class MagiskLogFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.recyclerView) RecyclerView rv;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
|
||||
unbinder = new MagiskLogFragment_ViewBinding(this, view);
|
||||
setHasOptionsMenu(true);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getActivity().setTitle(R.string.log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
readLogs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_log, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_refresh:
|
||||
readLogs();
|
||||
return true;
|
||||
case R.id.menu_save:
|
||||
runWithExternalRW(this::saveLogs);
|
||||
return true;
|
||||
case R.id.menu_clear:
|
||||
clearLogs();
|
||||
rv.setAdapter(new MagiskLogAdapter(NOPList.getInstance()));
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void readLogs() {
|
||||
Shell.su("tail -n 5000 " + Const.MAGISK_LOG).submit(result -> {
|
||||
rv.setAdapter(new MagiskLogAdapter(result.getOut()));
|
||||
});
|
||||
}
|
||||
|
||||
private void saveLogs() {
|
||||
Calendar now = Calendar.getInstance();
|
||||
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||
|
||||
File logFile = new File(Const.EXTERNAL_PATH, filename);
|
||||
try {
|
||||
logFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
Shell.su("cat " + Const.MAGISK_LOG + " > " + logFile)
|
||||
.submit(result ->
|
||||
SnackbarMaker.make(rv, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
private void clearLogs() {
|
||||
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
|
||||
SnackbarMaker.make(rv, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private class MagiskLogAdapter extends StringListAdapter<MagiskLogAdapter.ViewHolder> {
|
||||
|
||||
MagiskLogAdapter(List<String> list) {
|
||||
super(list);
|
||||
if (mList.isEmpty())
|
||||
mList.add(requireContext().getString(R.string.log_is_empty));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int itemLayoutRes() {
|
||||
return R.layout.list_item_console;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder createViewHolder(@NonNull View v) {
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateTextWidth(ViewHolder holder) {
|
||||
super.onUpdateTextWidth(holder);
|
||||
// Find the longest string and update accordingly
|
||||
int max = 0;
|
||||
String maxStr = "";
|
||||
for (String s : mList) {
|
||||
int len = s.length();
|
||||
if (len > max) {
|
||||
max = len;
|
||||
maxStr = s;
|
||||
}
|
||||
}
|
||||
holder.txt.setText(maxStr);
|
||||
super.onUpdateTextWidth(holder);
|
||||
}
|
||||
|
||||
public class ViewHolder extends StringListAdapter.ViewHolder {
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int textViewResId() {
|
||||
return R.id.txt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.topjohnwu.magisk.ui.log;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.SuLogAdapter;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class SuLogFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
|
||||
private SuLogAdapter adapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_log, menu);
|
||||
menu.findItem(R.id.menu_save).setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
|
||||
unbinder = new SuLogFragment_ViewBinding(this, v);
|
||||
adapter = new SuLogAdapter(app.mDB);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
updateList();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private void updateList() {
|
||||
adapter.notifyDBChanged();
|
||||
|
||||
if (adapter.getSectionCount() == 0) {
|
||||
emptyRv.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
emptyRv.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_refresh:
|
||||
updateList();
|
||||
return true;
|
||||
case R.id.menu_clear:
|
||||
app.mDB.clearLogs();
|
||||
updateList();
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.topjohnwu.magisk.ui.module;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.ModulesAdapter;
|
||||
import com.topjohnwu.magisk.model.entity.Module;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
import com.topjohnwu.magisk.utils.RootUtils;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class ModulesFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||
|
||||
@OnClick(R.id.fab)
|
||||
void selectFile() {
|
||||
runWithExternalRW(() -> {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("application/zip");
|
||||
startActivityForResult(intent, Const.ID.FETCH_ZIP);
|
||||
});
|
||||
}
|
||||
|
||||
private List<Module> listModules = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_modules, container, false);
|
||||
unbinder = new ModulesFragment_ViewBinding(this, view);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
Utils.loadModules();
|
||||
});
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
mSwipeRefreshLayout.setEnabled(recyclerView.getChildAt(0).getTop() >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||
super.onScrollStateChanged(recyclerView, newState);
|
||||
}
|
||||
});
|
||||
|
||||
requireActivity().setTitle(R.string.modules);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.MODULE_LOAD_DONE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
updateUI(Event.getResult(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
|
||||
// Get the URI of the selected file
|
||||
Intent intent = new Intent(getActivity(), ClassMap.get(FlashActivity.class));
|
||||
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_reboot, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.reboot:
|
||||
RootUtils.reboot();
|
||||
return true;
|
||||
case R.id.reboot_recovery:
|
||||
Shell.su("/system/bin/reboot recovery").submit();
|
||||
return true;
|
||||
case R.id.reboot_bootloader:
|
||||
Shell.su("/system/bin/reboot bootloader").submit();
|
||||
return true;
|
||||
case R.id.reboot_download:
|
||||
Shell.su("/system/bin/reboot download").submit();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(Map<String, Module> moduleMap) {
|
||||
listModules.clear();
|
||||
listModules.addAll(moduleMap.values());
|
||||
if (listModules.size() == 0) {
|
||||
emptyRv.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
emptyRv.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
recyclerView.setAdapter(new ModulesAdapter(listModules));
|
||||
}
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.topjohnwu.magisk.ui.module;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.ReposAdapter;
|
||||
import com.topjohnwu.magisk.tasks.UpdateRepos;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class ReposFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
private ReposAdapter adapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_repos, container, false);
|
||||
unbinder = new ReposFragment_ViewBinding(this, view);
|
||||
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> new UpdateRepos().exec(true));
|
||||
|
||||
adapter = new ReposAdapter();
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
|
||||
requireActivity().setTitle(R.string.downloads);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
Event.unregister(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.REPO_LOAD_DONE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
adapter.notifyDBChanged(false);
|
||||
Event.register(adapter);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
boolean empty = adapter.getItemCount() == 0;
|
||||
recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
|
||||
emptyRv.setVisibility(empty ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_repo, menu);
|
||||
SearchView search = (SearchView) menu.findItem(R.id.repo_search).getActionView();
|
||||
adapter.setSearchView(search);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.repo_sort) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.sorting_order)
|
||||
.setSingleChoiceItems(R.array.sorting_orders,
|
||||
Config.get(Config.Key.REPO_ORDER), (d, which) -> {
|
||||
Config.set(Config.Key.REPO_ORDER, which);
|
||||
adapter.notifyDBChanged(true);
|
||||
d.dismiss();
|
||||
}).show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package com.topjohnwu.magisk.ui.settings;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.BuildConfig;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.tasks.CheckUpdates;
|
||||
import com.topjohnwu.magisk.ui.base.BasePreferenceFragment;
|
||||
import com.topjohnwu.magisk.utils.DownloadApp;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||
import com.topjohnwu.magisk.utils.PatchAPK;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
public class SettingsFragment extends BasePreferenceFragment {
|
||||
|
||||
private ListPreference updateChannel, autoRes, suNotification,
|
||||
requestTimeout, rootConfig, multiuserConfig, nsConfig;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
||||
requireActivity().setTitle(R.string.settings);
|
||||
|
||||
boolean showSuperuser = Utils.showSuperUser();
|
||||
app.prefs.edit()
|
||||
.putBoolean(Config.Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint())
|
||||
.apply();
|
||||
|
||||
PreferenceScreen prefScreen = getPreferenceScreen();
|
||||
|
||||
PreferenceCategory generalCatagory = (PreferenceCategory) findPreference("general");
|
||||
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
|
||||
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
|
||||
Preference hideManager = findPreference("hide");
|
||||
hideManager.setOnPreferenceClickListener(pref -> {
|
||||
PatchAPK.hideManager();
|
||||
return true;
|
||||
});
|
||||
Preference restoreManager = findPreference("restore");
|
||||
restoreManager.setOnPreferenceClickListener(pref -> {
|
||||
DownloadApp.restore();
|
||||
return true;
|
||||
});
|
||||
findPreference("clear").setOnPreferenceClickListener(pref -> {
|
||||
app.prefs.edit().remove(Config.Key.ETAG_KEY).apply();
|
||||
app.repoDB.clearRepo();
|
||||
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
|
||||
return true;
|
||||
});
|
||||
findPreference("hosts").setOnPreferenceClickListener(pref -> {
|
||||
Shell.su("add_hosts_module").exec();
|
||||
Utils.loadModules();
|
||||
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT);
|
||||
return true;
|
||||
});
|
||||
|
||||
updateChannel = (ListPreference) findPreference(Config.Key.UPDATE_CHANNEL);
|
||||
rootConfig = (ListPreference) findPreference(Config.Key.ROOT_ACCESS);
|
||||
autoRes = (ListPreference) findPreference(Config.Key.SU_AUTO_RESPONSE);
|
||||
requestTimeout = (ListPreference) findPreference(Config.Key.SU_REQUEST_TIMEOUT);
|
||||
suNotification = (ListPreference) findPreference(Config.Key.SU_NOTIFICATION);
|
||||
multiuserConfig = (ListPreference) findPreference(Config.Key.SU_MULTIUSER_MODE);
|
||||
nsConfig = (ListPreference) findPreference(Config.Key.SU_MNT_NS);
|
||||
SwitchPreferenceCompat reauth = (SwitchPreferenceCompat) findPreference(Config.Key.SU_REAUTH);
|
||||
SwitchPreferenceCompat fingerprint = (SwitchPreferenceCompat) findPreference(Config.Key.SU_FINGERPRINT);
|
||||
|
||||
updateChannel.setOnPreferenceChangeListener((p, o) -> {
|
||||
int prev = Config.get(Config.Key.UPDATE_CHANNEL);
|
||||
int channel = Integer.parseInt((String) o);
|
||||
if (channel == Config.Value.CUSTOM_CHANNEL) {
|
||||
View v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null);
|
||||
EditText url = v.findViewById(R.id.custom_url);
|
||||
url.setText(app.prefs.getString(Config.Key.CUSTOM_CHANNEL, ""));
|
||||
new AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.settings_update_custom)
|
||||
.setView(v)
|
||||
.setPositiveButton(R.string.ok, (d, i) ->
|
||||
Config.set(Config.Key.CUSTOM_CHANNEL, url.getText().toString()))
|
||||
.setNegativeButton(R.string.close, (d, i) ->
|
||||
Config.set(Config.Key.UPDATE_CHANNEL, prev))
|
||||
.setOnCancelListener(d ->
|
||||
Config.set(Config.Key.UPDATE_CHANNEL, prev))
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
/* We only show canary channels if user is already on canary channel
|
||||
* or the user have already chosen canary channel */
|
||||
if (!Utils.isCanary() &&
|
||||
(int) Config.get(Config.Key.UPDATE_CHANNEL) < Config.Value.CANARY_CHANNEL) {
|
||||
// Remove the last 2 entries
|
||||
CharSequence[] entries = updateChannel.getEntries();
|
||||
updateChannel.setEntries(Arrays.copyOf(entries, entries.length - 2));
|
||||
}
|
||||
|
||||
setSummary();
|
||||
|
||||
// Disable dangerous settings in secondary user
|
||||
if (Const.USER_ID > 0) {
|
||||
suCategory.removePreference(multiuserConfig);
|
||||
}
|
||||
|
||||
// Disable re-authentication option on Android O, it will not work
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
reauth.setEnabled(false);
|
||||
reauth.setChecked(false);
|
||||
reauth.setSummary(R.string.android_o_not_support);
|
||||
}
|
||||
|
||||
// Disable fingerprint option if not possible
|
||||
if (!FingerprintHelper.canUseFingerprint()) {
|
||||
fingerprint.setEnabled(false);
|
||||
fingerprint.setChecked(false);
|
||||
fingerprint.setSummary(R.string.disable_fingerprint);
|
||||
}
|
||||
|
||||
if (Shell.rootAccess() && Const.USER_ID == 0) {
|
||||
if (app.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||
generalCatagory.removePreference(restoreManager);
|
||||
} else {
|
||||
if (!Networking.checkNetworkStatus(app))
|
||||
generalCatagory.removePreference(restoreManager);
|
||||
generalCatagory.removePreference(hideManager);
|
||||
}
|
||||
} else {
|
||||
generalCatagory.removePreference(restoreManager);
|
||||
generalCatagory.removePreference(hideManager);
|
||||
}
|
||||
|
||||
if (!showSuperuser) {
|
||||
prefScreen.removePreference(suCategory);
|
||||
}
|
||||
|
||||
if (!Shell.rootAccess()) {
|
||||
prefScreen.removePreference(magiskCategory);
|
||||
generalCatagory.removePreference(hideManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLocalePreference(ListPreference lp) {
|
||||
CharSequence[] entries = new CharSequence[LocaleManager.locales.size() + 1];
|
||||
CharSequence[] entryValues = new CharSequence[LocaleManager.locales.size() + 1];
|
||||
entries[0] = LocaleManager.getString(LocaleManager.defaultLocale, R.string.system_default);
|
||||
entryValues[0] = "";
|
||||
int i = 1;
|
||||
for (Locale locale : LocaleManager.locales) {
|
||||
entries[i] = locale.getDisplayName(locale);
|
||||
entryValues[i++] = LocaleManager.toLanguageTag(locale);
|
||||
}
|
||||
lp.setEntries(entries);
|
||||
lp.setEntryValues(entryValues);
|
||||
lp.setSummary(LocaleManager.locale.getDisplayName(LocaleManager.locale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||
switch (key) {
|
||||
case Config.Key.ROOT_ACCESS:
|
||||
case Config.Key.SU_MULTIUSER_MODE:
|
||||
case Config.Key.SU_MNT_NS:
|
||||
app.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
||||
break;
|
||||
case Config.Key.DARK_THEME:
|
||||
requireActivity().recreate();
|
||||
break;
|
||||
case Config.Key.COREONLY:
|
||||
if (prefs.getBoolean(key, false)) {
|
||||
try {
|
||||
Const.MAGISK_DISABLE_FILE.createNewFile();
|
||||
} catch (IOException ignored) {}
|
||||
} else {
|
||||
Const.MAGISK_DISABLE_FILE.delete();
|
||||
}
|
||||
Utils.toast(R.string.settings_reboot_toast, Toast.LENGTH_LONG);
|
||||
break;
|
||||
case Config.Key.MAGISKHIDE:
|
||||
if (prefs.getBoolean(key, false)) {
|
||||
Shell.su("magiskhide --enable").submit();
|
||||
} else {
|
||||
Shell.su("magiskhide --disable").submit();
|
||||
}
|
||||
break;
|
||||
case Config.Key.LOCALE:
|
||||
LocaleManager.setLocale(app);
|
||||
requireActivity().recreate();
|
||||
break;
|
||||
case Config.Key.UPDATE_CHANNEL:
|
||||
case Config.Key.CUSTOM_CHANNEL:
|
||||
CheckUpdates.check();
|
||||
break;
|
||||
case Config.Key.CHECK_UPDATES:
|
||||
Utils.scheduleUpdateCheck();
|
||||
break;
|
||||
}
|
||||
setSummary(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
String key = preference.getKey();
|
||||
switch (key) {
|
||||
case Config.Key.SU_FINGERPRINT:
|
||||
boolean checked = ((SwitchPreferenceCompat) preference).isChecked();
|
||||
((SwitchPreferenceCompat) preference).setChecked(!checked);
|
||||
new FingerprintAuthDialog(requireActivity(), () -> {
|
||||
((SwitchPreferenceCompat) preference).setChecked(checked);
|
||||
Config.set(key, checked);
|
||||
}).show();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setSummary(String key) {
|
||||
switch (key) {
|
||||
case Config.Key.UPDATE_CHANNEL:
|
||||
int ch = Config.get(key);
|
||||
ch = ch < 0 ? Config.Value.STABLE_CHANNEL : ch;
|
||||
updateChannel.setSummary(getResources()
|
||||
.getStringArray(R.array.update_channel)[ch]);
|
||||
break;
|
||||
case Config.Key.ROOT_ACCESS:
|
||||
rootConfig.setSummary(getResources()
|
||||
.getStringArray(R.array.su_access)[(int)Config.get(key)]);
|
||||
break;
|
||||
case Config.Key.SU_AUTO_RESPONSE:
|
||||
autoRes.setSummary(getResources()
|
||||
.getStringArray(R.array.auto_response)[(int)Config.get(key)]);
|
||||
break;
|
||||
case Config.Key.SU_NOTIFICATION:
|
||||
suNotification.setSummary(getResources()
|
||||
.getStringArray(R.array.su_notification)[(int)Config.get(key)]);
|
||||
break;
|
||||
case Config.Key.SU_REQUEST_TIMEOUT:
|
||||
requestTimeout.setSummary(
|
||||
getString(R.string.request_timeout_summary, (int)Config.get(key)));
|
||||
break;
|
||||
case Config.Key.SU_MULTIUSER_MODE:
|
||||
multiuserConfig.setSummary(getResources()
|
||||
.getStringArray(R.array.multiuser_summary)[(int)Config.get(key)]);
|
||||
break;
|
||||
case Config.Key.SU_MNT_NS:
|
||||
nsConfig.setSummary(getResources()
|
||||
.getStringArray(R.array.namespace_summary)[(int)Config.get(key)]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setSummary() {
|
||||
setSummary(Config.Key.UPDATE_CHANNEL);
|
||||
setSummary(Config.Key.ROOT_ACCESS);
|
||||
setSummary(Config.Key.SU_AUTO_RESPONSE);
|
||||
setSummary(Config.Key.SU_NOTIFICATION);
|
||||
setSummary(Config.Key.SU_REQUEST_TIMEOUT);
|
||||
setSummary(Config.Key.SU_MULTIUSER_MODE);
|
||||
setSummary(Config.Key.SU_MNT_NS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
setLocalePreference((ListPreference) findPreference(Config.Key.LOCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.LOCALE_FETCH_DONE};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.topjohnwu.magisk.ui.superuser;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.adapters.PolicyAdapter;
|
||||
import com.topjohnwu.magisk.model.entity.Policy;
|
||||
import com.topjohnwu.magisk.ui.base.BaseFragment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
|
||||
public class SuperuserFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||
|
||||
private PackageManager pm;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
|
||||
unbinder = new SuperuserFragment_ViewBinding(this, view);
|
||||
|
||||
pm = requireActivity().getPackageManager();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(getString(R.string.superuser));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
displayPolicyList();
|
||||
}
|
||||
|
||||
private void displayPolicyList() {
|
||||
List<Policy> policyList = app.mDB.getPolicyList();
|
||||
|
||||
if (policyList.size() == 0) {
|
||||
emptyRv.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
recyclerView.setAdapter(new PolicyAdapter(policyList, app.mDB, pm));
|
||||
emptyRv.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package com.topjohnwu.magisk.ui.surequest;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.BuildConfig;
|
||||
import com.topjohnwu.magisk.Config;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.entity.Policy;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||
import com.topjohnwu.magisk.utils.SuConnector;
|
||||
import com.topjohnwu.magisk.utils.SuLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import butterknife.BindView;
|
||||
import java9.lang.Iterables;
|
||||
|
||||
public class SuRequestActivity extends BaseActivity {
|
||||
|
||||
@BindView(R.id.su_popup) LinearLayout suPopup;
|
||||
@BindView(R.id.timeout) Spinner timeout;
|
||||
@BindView(R.id.app_icon) ImageView appIcon;
|
||||
@BindView(R.id.app_name) TextView appNameView;
|
||||
@BindView(R.id.package_name) TextView packageNameView;
|
||||
@BindView(R.id.grant_btn) Button grant_btn;
|
||||
@BindView(R.id.deny_btn) Button deny_btn;
|
||||
@BindView(R.id.fingerprint) ImageView fingerprintImg;
|
||||
@BindView(R.id.warning) TextView warning;
|
||||
|
||||
private ActionHandler handler;
|
||||
private Policy policy;
|
||||
private SharedPreferences timeoutPrefs;
|
||||
|
||||
public static final String REQUEST = "request";
|
||||
public static final String LOG = "log";
|
||||
public static final String NOTIFY = "notify";
|
||||
|
||||
@Override
|
||||
public int getDarkTheme() {
|
||||
return R.style.SuRequest_Dark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
handler.handleAction(Policy.DENY, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
lockOrientation();
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
timeoutPrefs = App.deContext.getSharedPreferences("su_timeout", 0);
|
||||
Intent intent = getIntent();
|
||||
|
||||
String action = intent.getAction();
|
||||
|
||||
if (TextUtils.equals(action, REQUEST)) {
|
||||
if (!handleRequest())
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(action, LOG))
|
||||
SuLogger.handleLogs(intent);
|
||||
else if (TextUtils.equals(action, NOTIFY))
|
||||
SuLogger.handleNotify(intent);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private boolean handleRequest() {
|
||||
String socketName = getIntent().getStringExtra("socket");
|
||||
|
||||
if (socketName == null)
|
||||
return false;
|
||||
|
||||
SuConnector connector;
|
||||
try {
|
||||
connector = new SuConnector(socketName) {
|
||||
@Override
|
||||
protected void onResponse() throws IOException {
|
||||
out.writeInt(policy.policy);
|
||||
}
|
||||
};
|
||||
Bundle bundle = connector.readSocketInput();
|
||||
int uid = Integer.parseInt(bundle.getString("uid"));
|
||||
app.mDB.clearOutdated();
|
||||
policy = app.mDB.getPolicy(uid);
|
||||
if (policy == null) {
|
||||
policy = new Policy(uid, getPackageManager());
|
||||
}
|
||||
} catch (IOException | PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
handler = new ActionHandler() {
|
||||
@Override
|
||||
void handleAction() {
|
||||
connector.response();
|
||||
done();
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleAction(int action) {
|
||||
int pos = timeout.getSelectedItemPosition();
|
||||
timeoutPrefs.edit().putInt(policy.packageName, pos).apply();
|
||||
handleAction(action, Config.Value.TIMEOUT_LIST[pos]);
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleAction(int action, int time) {
|
||||
policy.policy = action;
|
||||
if (time >= 0) {
|
||||
policy.until = (time == 0) ? 0
|
||||
: (System.currentTimeMillis() / 1000 + time * 60);
|
||||
app.mDB.updatePolicy(policy);
|
||||
}
|
||||
handleAction();
|
||||
}
|
||||
};
|
||||
|
||||
// Never allow com.topjohnwu.magisk (could be malware)
|
||||
if (TextUtils.equals(policy.packageName, BuildConfig.APPLICATION_ID))
|
||||
return false;
|
||||
|
||||
// If not interactive, response directly
|
||||
if (policy.policy != Policy.INTERACTIVE) {
|
||||
handler.handleAction();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ((int) Config.get(Config.Key.SU_AUTO_RESPONSE)) {
|
||||
case Config.Value.SU_AUTO_DENY:
|
||||
handler.handleAction(Policy.DENY, 0);
|
||||
return true;
|
||||
case Config.Value.SU_AUTO_ALLOW:
|
||||
handler.handleAction(Policy.ALLOW, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
showUI();
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void showUI() {
|
||||
setContentView(R.layout.activity_request);
|
||||
new SuRequestActivity_ViewBinding(this);
|
||||
|
||||
appIcon.setImageDrawable(policy.info.loadIcon(getPackageManager()));
|
||||
appNameView.setText(policy.appName);
|
||||
packageNameView.setText(policy.packageName);
|
||||
warning.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
AppCompatResources.getDrawable(this, R.drawable.ic_warning), null, null, null);
|
||||
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
|
||||
R.array.allow_timeout, android.R.layout.simple_spinner_item);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
timeout.setAdapter(adapter);
|
||||
timeout.setSelection(timeoutPrefs.getInt(policy.packageName, 0));
|
||||
|
||||
CountDownTimer timer = new CountDownTimer(
|
||||
(int) Config.get(Config.Key.SU_REQUEST_TIMEOUT) * 1000, 1000) {
|
||||
@Override
|
||||
public void onTick(long remains) {
|
||||
deny_btn.setText(getString(R.string.deny) + "(" + remains / 1000 + ")");
|
||||
}
|
||||
@Override
|
||||
public void onFinish() {
|
||||
deny_btn.setText(getString(R.string.deny));
|
||||
handler.handleAction(Policy.DENY);
|
||||
}
|
||||
};
|
||||
timer.start();
|
||||
Runnable cancelTimer = () -> {
|
||||
timer.cancel();
|
||||
deny_btn.setText(getString(R.string.deny));
|
||||
};
|
||||
handler.addCancel(cancelTimer);
|
||||
|
||||
boolean useFP = FingerprintHelper.useFingerprint();
|
||||
|
||||
if (useFP) try {
|
||||
FingerprintHelper helper = new SuFingerprint();
|
||||
helper.authenticate();
|
||||
handler.addCancel(helper::cancel);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
useFP = false;
|
||||
}
|
||||
|
||||
if (!useFP) {
|
||||
grant_btn.setOnClickListener(v -> {
|
||||
handler.handleAction(Policy.ALLOW);
|
||||
timer.cancel();
|
||||
});
|
||||
grant_btn.requestFocus();
|
||||
}
|
||||
|
||||
grant_btn.setVisibility(useFP ? View.GONE : View.VISIBLE);
|
||||
fingerprintImg.setVisibility(useFP ? View.VISIBLE : View.GONE);
|
||||
|
||||
deny_btn.setOnClickListener(v -> {
|
||||
handler.handleAction(Policy.DENY);
|
||||
timer.cancel();
|
||||
});
|
||||
suPopup.setOnClickListener(v -> cancelTimer.run());
|
||||
timeout.setOnTouchListener((v, event) -> {
|
||||
cancelTimer.run();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private class SuFingerprint extends FingerprintHelper {
|
||||
|
||||
SuFingerprint() throws Exception {}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
||||
warning.setText(errString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
||||
warning.setText(helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
||||
handler.handleAction(Policy.ALLOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
warning.setText(R.string.auth_fail);
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionHandler {
|
||||
private List<Runnable> cancelTasks = new ArrayList<>();
|
||||
|
||||
void handleAction() {
|
||||
done();
|
||||
}
|
||||
|
||||
void handleAction(int action) {
|
||||
done();
|
||||
}
|
||||
|
||||
void handleAction(int action, int time) {
|
||||
done();
|
||||
}
|
||||
|
||||
void addCancel(Runnable r) {
|
||||
cancelTasks.add(r);
|
||||
}
|
||||
|
||||
void done() {
|
||||
Iterables.forEach(cancelTasks, Runnable::run);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user