diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a8185d57..13e09553 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -197,12 +197,15 @@ dependencies {
val glideVersion = "4.12.0"
val markwonVersion = "4.6.2"
val okhttpVersion = "4.9.1"
+ val navVersion = "2.3.5"
annotationProcessor("com.github.bumptech.glide:compiler:$glideVersion")
implementation("androidx.activity:activity:1.2.3")
implementation("androidx.browser:browser:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
implementation("androidx.core:core:1.5.0")
implementation("androidx.fragment:fragment:1.3.4")
+ implementation("androidx.navigation:navigation-fragment:$navVersion")
+ implementation("androidx.navigation:navigation-ui:$navVersion")
implementation("androidx.recyclerview:recyclerview:1.2.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("com.caverock:androidsvg-aar:1.4")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1c30ab60..f917a880 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -26,7 +26,8 @@
-
@@ -45,45 +46,24 @@
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning"
tools:targetApi="q">
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+ android:name=".ui.activity.CrashReportActivity"
+ android:process=":error_activity" />
implements Filterable, Handler.Callback {
- private final AppListActivity activity;
+ private final Activity activity;
+ private final AppListFragment fragment;
private final PackageManager pm;
private final SharedPreferences preferences;
private final Handler loadAppListHandler;
@@ -121,9 +123,9 @@ public class ScopeAdapter extends RecyclerView.Adapter
@Override
public void run() {
synchronized (this) {
- activity.binding.progress.setIndeterminate(false);
- activity.binding.swipeRefreshLayout.setRefreshing(false);
- String queryStr = activity.searchView != null ? activity.searchView.getQuery().toString() : "";
+ fragment.binding.progress.setIndeterminate(false);
+ fragment.binding.swipeRefreshLayout.setRefreshing(false);
+ String queryStr = fragment.searchView != null ? fragment.searchView.getQuery().toString() : "";
getFilter().filter(queryStr);
this.notify();
}
@@ -134,8 +136,9 @@ public class ScopeAdapter extends RecyclerView.Adapter
private boolean refreshing = false;
private boolean enabled = true;
- public ScopeAdapter(AppListActivity activity, ModuleUtil.InstalledModule module) {
- this.activity = activity;
+ public ScopeAdapter(AppListFragment fragment, ModuleUtil.InstalledModule module) {
+ this.fragment = fragment;
+ this.activity = fragment.requireActivity();
this.module = module;
moduleUtil = ModuleUtil.getInstance();
HandlerThread handlerThread = new HandlerThread("appList");
@@ -250,12 +253,12 @@ public class ScopeAdapter extends RecyclerView.Adapter
if (launchIntent != null) {
ConfigManager.startActivityAsUserWithFeature(launchIntent, module.userId);
} else {
- activity.makeSnackBar(R.string.module_no_ui, Snackbar.LENGTH_LONG);
+ fragment.makeSnackBar(R.string.module_no_ui, Snackbar.LENGTH_LONG);
}
return true;
} else if (itemId == R.id.backup) {
Calendar now = Calendar.getInstance();
- activity.backupLauncher.launch(String.format(Locale.US,
+ fragment.backupLauncher.launch(String.format(Locale.US,
"%s_%04d%02d%02d_%02d%02d%02d.lsp",
module.getAppName(),
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
@@ -263,7 +266,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
now.get(Calendar.MINUTE), now.get(Calendar.SECOND)));
return true;
} else if (itemId == R.id.restore) {
- activity.restoreLauncher.launch(new String[]{"*/*"});
+ fragment.restoreLauncher.launch(new String[]{"*/*"});
return true;
} else if (!AppHelper.onOptionsItemSelected(item, preferences)) {
return false;
@@ -284,7 +287,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
ConfigManager.startActivityAsUserWithFeature(launchIntent, module.userId);
}
} else if (itemId == R.id.menu_compile_speed) {
- CompileDialogFragment.speed(activity.getSupportFragmentManager(), info);
+ CompileDialogFragment.speed(fragment.getChildFragmentManager(), info);
} else if (itemId == R.id.menu_other_app) {
var intent = new Intent(Intent.ACTION_SHOW_APP_INFO);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, module.packageName);
@@ -309,8 +312,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
return true;
}
- public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
- inflater.inflate(R.menu.menu_app_list, menu);
+ public void onPrepareOptionsMenu(@NonNull Menu menu) {
Intent intent = AppHelper.getSettingsIntent(module.packageName, module.userId);
if (intent == null) {
menu.removeItem(R.id.menu_launch);
@@ -448,14 +450,14 @@ public class ScopeAdapter extends RecyclerView.Adapter
}
loadAppListHandler.removeMessages(0);
if (!force) {
- activity.binding.progress.setVisibility(View.INVISIBLE);
- activity.binding.progress.setIndeterminate(true);
- activity.binding.progress.setVisibility(View.VISIBLE);
+ fragment.binding.progress.setVisibility(View.INVISIBLE);
+ fragment.binding.progress.setIndeterminate(true);
+ fragment.binding.progress.setVisibility(View.VISIBLE);
}
enabled = moduleUtil.isModuleEnabled(module.packageName);
- activity.binding.masterSwitch.setOnCheckedChangeListener(null);
- activity.binding.masterSwitch.setChecked(enabled);
- activity.binding.masterSwitch.setOnCheckedChangeListener(switchBarOnCheckedChangeListener);
+ fragment.binding.masterSwitch.setOnCheckedChangeListener(null);
+ fragment.binding.masterSwitch.setChecked(enabled);
+ fragment.binding.masterSwitch.setOnCheckedChangeListener(switchBarOnCheckedChangeListener);
loadAppListHandler.sendMessage(Message.obtain(loadAppListHandler, 0, force));
}
@@ -466,7 +468,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
checkedList.remove(appInfo.application);
}
if (!ConfigManager.setModuleScope(module.packageName, checkedList)) {
- activity.makeSnackBar(R.string.failed_to_save_scope_list, Snackbar.LENGTH_SHORT);
+ fragment.makeSnackBar(R.string.failed_to_save_scope_list, Snackbar.LENGTH_SHORT);
if (!isChecked) {
checkedList.add(appInfo.application);
} else {
@@ -474,7 +476,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
}
buttonView.setChecked(!isChecked);
} else if (appInfo.packageName.equals("android")) {
- Snackbar.make(activity.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT)
+ Snackbar.make(fragment.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT)
.setAction(R.string.reboot, v -> ConfigManager.reboot(false, null, false))
.show();
}
@@ -619,7 +621,7 @@ public class ScopeAdapter extends RecyclerView.Adapter
}
public boolean onBackPressed() {
- if (!refreshing && activity.binding.masterSwitch.isChecked() && checkedList.isEmpty()) {
+ if (!refreshing && fragment.binding.masterSwitch.isChecked() && checkedList.isEmpty()) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(!recommendedList.isEmpty() ? R.string.no_scope_selected_has_recommended : R.string.no_scope_selected);
if (!recommendedList.isEmpty()) {
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java
index cd091607..51b89081 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java
@@ -1,157 +1,126 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
-
package org.lsposed.manager.ui.activity;
+import android.annotation.SuppressLint;
+import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
-import android.text.method.LinkMovementMethod;
-import android.view.LayoutInflater;
-import android.view.View;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
-import androidx.core.text.HtmlCompat;
+import androidx.annotation.NonNull;
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+import androidx.navigation.fragment.NavHostFragment;
-import com.bumptech.glide.Glide;
-import com.google.android.material.snackbar.Snackbar;
-
-import org.lsposed.manager.BuildConfig;
-import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.databinding.ActivityMainBinding;
-import org.lsposed.manager.databinding.DialogAboutBinding;
import org.lsposed.manager.ui.activity.base.BaseActivity;
-import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;
-import org.lsposed.manager.ui.dialog.InfoDialogBuilder;
-import org.lsposed.manager.util.GlideHelper;
-import org.lsposed.manager.util.ModuleUtil;
-import org.lsposed.manager.util.NavUtil;
-import org.lsposed.manager.util.chrome.LinkTransformationMethod;
-
-import java.util.Locale;
-
-import rikka.core.res.ResourcesKt;
public class MainActivity extends BaseActivity {
- ActivityMainBinding binding;
+ private static final String KEY_PREFIX = MainActivity.class.getName() + '.';
+ private static final String EXTRA_SAVED_INSTANCE_STATE = KEY_PREFIX + "SAVED_INSTANCE_STATE";
+ private boolean restarting;
+ private ActivityMainBinding binding;
+
+ @NonNull
+ public static Intent newIntent(@NonNull Context context) {
+ return new Intent(context, MainActivity.class);
+ }
+
+ @NonNull
+ private static Intent newIntent(@NonNull Bundle savedInstanceState, @NonNull Context context) {
+ return newIntent(context)
+ .putExtra(EXTRA_SAVED_INSTANCE_STATE, savedInstanceState);
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ savedInstanceState = getIntent().getBundleExtra(EXTRA_SAVED_INSTANCE_STATE);
+ }
super.onCreate(savedInstanceState);
+
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
- binding.status.setOnClickListener(v -> {
- if (ConfigManager.getXposedApiVersion() != -1) {
- new InfoDialogBuilder(this)
- .setTitle(R.string.info)
- .show();
- } else {
- NavUtil.startURL(this, getString(R.string.about_source));
- }
- });
- binding.modules.setOnClickListener(new StartActivityListener(ModulesActivity.class, true));
- binding.download.setOnClickListener(new StartActivityListener(RepoActivity.class, false));
- binding.logs.setOnClickListener(new StartActivityListener(LogsActivity.class, true));
- binding.settings.setOnClickListener(new StartActivityListener(SettingsActivity.class, false));
- binding.about.setOnClickListener(v -> {
- DialogAboutBinding binding = DialogAboutBinding.inflate(LayoutInflater.from(this), null, false);
- binding.sourceCode.setMovementMethod(LinkMovementMethod.getInstance());
- binding.sourceCode.setTransformationMethod(new LinkTransformationMethod(this));
- binding.sourceCode.setText(HtmlCompat.fromHtml(getString(
- R.string.about_view_source_code,
- "GitHub",
- "Telegram"), HtmlCompat.FROM_HTML_MODE_LEGACY));
- binding.translators.setMovementMethod(LinkMovementMethod.getInstance());
- binding.translators.setTransformationMethod(new LinkTransformationMethod(this));
- binding.translators.setText(HtmlCompat.fromHtml(getString(R.string.about_translators, getString(R.string.translators)), HtmlCompat.FROM_HTML_MODE_LEGACY));
- binding.version.setText(String.format(Locale.US, "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
- new BlurBehindDialogBuilder(this)
- .setView(binding.getRoot())
- .show();
- });
- Glide.with(binding.appIcon)
- .load(GlideHelper.wrapApplicationInfoForIconLoader(getApplicationInfo()))
- .into(binding.appIcon);
- String installXposedVersion = ConfigManager.getXposedVersionName();
- int cardBackgroundColor;
- if (installXposedVersion != null) {
- if (!ConfigManager.isSepolicyLoaded()) {
- binding.statusTitle.setText(R.string.partial_activated);
- cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorWarning);
- binding.statusIcon.setImageResource(R.drawable.ic_warning);
- binding.statusSummary.setText(R.string.selinux_policy_not_loaded_summary);
- } else if (!ConfigManager.systemServerRequested()) {
- binding.statusTitle.setText(R.string.partial_activated);
- cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorWarning);
- binding.statusIcon.setImageResource(R.drawable.ic_warning);
- binding.statusSummary.setText(R.string.system_inject_fail_summary);
- } else {
- binding.statusTitle.setText(R.string.activated);
- cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorNormal);
- binding.statusIcon.setImageResource(R.drawable.ic_check_circle);
- binding.statusSummary.setText(String.format(Locale.US, "%s (%d)", installXposedVersion, ConfigManager.getXposedVersionCode()));
- }
- } else {
- cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorInstall);
- boolean isMagiskInstalled = ConfigManager.isMagiskInstalled();
- binding.statusTitle.setText(isMagiskInstalled ? R.string.Install : R.string.NotInstall);
- binding.statusSummary.setText(isMagiskInstalled ? R.string.InstallDetail : R.string.NotInstallDetail);
- if (!isMagiskInstalled) {
- binding.status.setOnClickListener(null);
- binding.download.setVisibility(View.GONE);
- }
- binding.statusIcon.setImageResource(R.drawable.ic_error);
- Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_INDEFINITE).show();
- }
- binding.status.setCardBackgroundColor(cardBackgroundColor);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- binding.status.setOutlineSpotShadowColor(cardBackgroundColor);
- binding.status.setOutlineAmbientShadowColor(cardBackgroundColor);
+
+ if (savedInstanceState == null) {
+ handleIntent(getIntent());
}
}
- private class StartActivityListener implements View.OnClickListener {
- boolean requireInstalled;
- Class> clazz;
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
- StartActivityListener(Class> clazz, boolean requireInstalled) {
- this.clazz = clazz;
- this.requireInstalled = requireInstalled;
+ private void handleIntent(Intent intent) {
+ if (intent == null) {
+ return;
}
-
- @Override
- public void onClick(View v) {
- if (requireInstalled && ConfigManager.getXposedVersionName() == null) {
- Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show();
- } else {
- Intent intent = new Intent();
- intent.setClass(MainActivity.this, clazz);
- startActivity(intent);
+ NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
+ NavController navController = navHostFragment.getNavController();
+ if (intent.getAction().equals("android.intent.action.APPLICATION_PREFERENCES")) {
+ navController.navigate(R.id.settings_fragment);
+ } else if (intent.hasExtra("modulePackageName")) {
+ Bundle bundle = new Bundle();
+ bundle.putString("modulePackageName", intent.getStringExtra("modulePackageName"));
+ bundle.putInt("moduleUserId", intent.getIntExtra("moduleUserId", -1));
+ navController.navigate(R.id.app_list_fragment, bundle);
+ } else if (!TextUtils.isEmpty(intent.getDataString())) {
+ switch (intent.getDataString()) {
+ case "modules":
+ navController.navigate(R.id.modules_fragment);
+ break;
+ case "logs":
+ navController.navigate(R.id.logs_fragment);
+ break;
+ case "repo":
+ navController.navigate(R.id.repo_fragment);
+ break;
}
}
}
@Override
- protected void onResume() {
- super.onResume();
- int moduleCount = ModuleUtil.getInstance().getEnabledModulesCount();
- binding.modulesSummary.setText(getResources().getQuantityString(R.plurals.modules_enabled_count, moduleCount, moduleCount));
+ public boolean onSupportNavigateUp() {
+ NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
+ return navController.navigateUp() || super.onSupportNavigateUp();
+ }
+
+ public void restart() {
+ Bundle savedInstanceState = new Bundle();
+ onSaveInstanceState(savedInstanceState);
+ finish();
+ startActivity(newIntent(savedInstanceState, this));
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ restarting = true;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+ return restarting || super.dispatchKeyEvent(event);
+ }
+
+ @SuppressLint("RestrictedApi")
+ @Override
+ public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) {
+ return restarting || super.dispatchKeyShortcutEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
+ return restarting || super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(@NonNull MotionEvent event) {
+ return restarting || super.dispatchTrackballEvent(event);
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) {
+ return restarting || super.dispatchGenericMotionEvent(event);
}
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/SettingsActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/SettingsActivity.java
index 297d2379..a42fd347 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/SettingsActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/activity/SettingsActivity.java
@@ -51,7 +51,7 @@ import com.takisoft.preferencex.PreferenceFragmentCompat;
import org.lsposed.manager.BuildConfig;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
-import org.lsposed.manager.databinding.ActivitySettingsBinding;
+import org.lsposed.manager.databinding.FragmentSettingsBinding;
import org.lsposed.manager.ui.activity.base.BaseActivity;
import org.lsposed.manager.util.BackupUtils;
import org.lsposed.manager.util.theme.ThemeUtil;
@@ -67,7 +67,7 @@ import rikka.widget.borderview.BorderRecyclerView;
public class SettingsActivity extends BaseActivity {
private static final String KEY_PREFIX = SettingsActivity.class.getName() + '.';
private static final String EXTRA_SAVED_INSTANCE_STATE = KEY_PREFIX + "SAVED_INSTANCE_STATE";
- ActivitySettingsBinding binding;
+ FragmentSettingsBinding binding;
private boolean restarting;
@NonNull
@@ -87,7 +87,7 @@ public class SettingsActivity extends BaseActivity {
savedInstanceState = getIntent().getBundleExtra(EXTRA_SAVED_INSTANCE_STATE);
}
super.onCreate(savedInstanceState);
- binding = ActivitySettingsBinding.inflate(getLayoutInflater());
+ binding = FragmentSettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setAppBar(binding.appBar, binding.toolbar);
binding.getRoot().bringChildToFront(binding.appBar);
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/base/ListActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/base/ListActivity.java
deleted file mode 100644
index abf13657..00000000
--- a/app/src/main/java/org/lsposed/manager/ui/activity/base/ListActivity.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
-
-package org.lsposed.manager.ui.activity.base;
-
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.View;
-import android.widget.Filterable;
-
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.widget.SearchView;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.lsposed.manager.R;
-import org.lsposed.manager.databinding.ActivityListBinding;
-import org.lsposed.manager.util.LinearLayoutManagerFix;
-
-import rikka.recyclerview.RecyclerViewKt;
-
-public abstract class ListActivity extends BaseActivity {
-
- protected ActivityListBinding binding;
- protected SearchView searchView;
- private SearchView.OnQueryTextListener mSearchListener;
- private BaseAdapter> adapter = null;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding = ActivityListBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
- setAppBar(binding.appBar, binding.toolbar);
- binding.getRoot().bringChildToFront(binding.appBar);
- binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
- binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
- ActionBar bar = getSupportActionBar();
- if (bar != null) {
- bar.setDisplayHomeAsUpEnabled(true);
- }
- adapter = createAdapter();
- adapter.setHasStableIds(true);
- binding.recyclerView.setAdapter(adapter);
- binding.recyclerView.setHasFixedSize(true);
- binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(this));
- RecyclerViewKt.addFastScroller(binding.recyclerView, binding.recyclerView);
- RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
- binding.progress.setVisibilityAfterHide(View.GONE);
- mSearchListener = new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- adapter.getFilter().filter(query);
- return false;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- adapter.getFilter().filter(newText);
- return false;
- }
- };
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
- searchView.setOnQueryTextListener(mSearchListener);
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- public void onBackPressed() {
- if (searchView.isIconified()) {
- super.onBackPressed();
- } else {
- searchView.setIconified(true);
- }
- }
-
- protected abstract static class BaseAdapter extends RecyclerView.Adapter implements Filterable {
-
- }
-
- protected abstract BaseAdapter> createAdapter();
-}
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java b/app/src/main/java/org/lsposed/manager/ui/fragment/AppListFragment.java
similarity index 65%
rename from app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java
rename to app/src/main/java/org/lsposed/manager/ui/fragment/AppListFragment.java
index 3aa4c6df..618e243b 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/AppListFragment.java
@@ -1,37 +1,19 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
-
-package org.lsposed.manager.ui.activity;
+package org.lsposed.manager.ui.fragment;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
@@ -40,8 +22,7 @@ import com.google.android.material.snackbar.Snackbar;
import org.lsposed.manager.BuildConfig;
import org.lsposed.manager.R;
import org.lsposed.manager.adapters.ScopeAdapter;
-import org.lsposed.manager.databinding.ActivityAppListBinding;
-import org.lsposed.manager.ui.activity.base.BaseActivity;
+import org.lsposed.manager.databinding.FragmentAppListBinding;
import org.lsposed.manager.util.BackupUtils;
import org.lsposed.manager.util.LinearLayoutManagerFix;
import org.lsposed.manager.util.ModuleUtil;
@@ -50,68 +31,74 @@ import java.util.Locale;
import rikka.recyclerview.RecyclerViewKt;
-public class AppListActivity extends BaseActivity {
+public class AppListFragment extends BaseFragment {
+
public SearchView searchView;
private ScopeAdapter scopeAdapter;
+ private ModuleUtil.InstalledModule module;
private SearchView.OnQueryTextListener searchListener;
- public ActivityAppListBinding binding;
+ public FragmentAppListBinding binding;
public ActivityResultLauncher backupLauncher;
public ActivityResultLauncher restoreLauncher;
+ @Nullable
@Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- String modulePackageName = getIntent().getStringExtra("modulePackageName");
- int moduleUserId = getIntent().getIntExtra("moduleUserId", -1);
- binding = ActivityAppListBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
- setAppBar(binding.appBar, binding.toolbar);
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentAppListBinding.inflate(getLayoutInflater(), container, false);
binding.appBar.setRaised(true);
- binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
- ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName, moduleUserId);
- if (module == null) {
- finish();
- return;
- }
- ActionBar bar = getSupportActionBar();
- if (bar != null) {
- bar.setDisplayHomeAsUpEnabled(true);
- if (module.userId != 0) {
- bar.setTitle(String.format(Locale.US, "%s (%d)", module.getAppName(), module.userId));
- } else {
- bar.setTitle(module.getAppName());
- }
- bar.setSubtitle(module.packageName);
+ String title;
+ if (module.userId != 0) {
+ title = String.format(Locale.US, "%s (%d)", module.getAppName(), module.userId);
+ } else {
+ title = module.getAppName();
}
+ binding.toolbar.setSubtitle(module.packageName);
+
scopeAdapter = new ScopeAdapter(this, module);
scopeAdapter.setHasStableIds(true);
binding.recyclerView.setAdapter(scopeAdapter);
binding.recyclerView.setHasFixedSize(true);
- binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(this));
+ binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(requireActivity()));
RecyclerViewKt.addFastScroller(binding.recyclerView, binding.recyclerView);
RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
binding.swipeRefreshLayout.setOnRefreshListener(() -> scopeAdapter.refresh(true));
searchListener = scopeAdapter.getSearchListener();
+ setupToolbar(binding.toolbar, title, R.menu.menu_app_list);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ String modulePackageName = getArguments().getString("modulePackageName");
+ int moduleUserId = getArguments().getInt("moduleUserId", -1);
+
+ module = ModuleUtil.getInstance().getModule(modulePackageName, moduleUserId);
+ if (module == null) {
+ getNavController().navigateUp();
+ return;
+ }
+
backupLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
uri -> {
if (uri != null) {
try {
// grantUriPermission might throw RemoteException on MIUI
- grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ requireActivity().grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} catch (Exception e) {
e.printStackTrace();
}
- AlertDialog alertDialog = new AlertDialog.Builder(this)
+ AlertDialog alertDialog = new AlertDialog.Builder(requireActivity())
.setCancelable(false)
.setMessage(R.string.settings_backuping)
.show();
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- boolean success = BackupUtils.backup(this, uri, modulePackageName);
+ boolean success = BackupUtils.backup(requireActivity(), uri, modulePackageName);
try {
- runOnUiThread(() -> {
+ requireActivity().runOnUiThread(() -> {
alertDialog.dismiss();
makeSnackBar(success ? R.string.settings_backup_success : R.string.settings_backup_failed, Snackbar.LENGTH_SHORT);
});
@@ -126,18 +113,18 @@ public class AppListActivity extends BaseActivity {
if (uri != null) {
try {
// grantUriPermission might throw RemoteException on MIUI
- grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ requireActivity().grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (Exception e) {
e.printStackTrace();
}
- AlertDialog alertDialog = new AlertDialog.Builder(this)
+ AlertDialog alertDialog = new AlertDialog.Builder(requireActivity())
.setCancelable(false)
.setMessage(R.string.settings_restoring)
.show();
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- boolean success = BackupUtils.restore(this, uri, modulePackageName);
+ boolean success = BackupUtils.restore(requireActivity(), uri, modulePackageName);
try {
- runOnUiThread(() -> {
+ requireActivity().runOnUiThread(() -> {
alertDialog.dismiss();
makeSnackBar(success ? R.string.settings_restore_success : R.string.settings_restore_failed, Snackbar.LENGTH_SHORT);
scopeAdapter.refresh(false);
@@ -151,7 +138,7 @@ public class AppListActivity extends BaseActivity {
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
scopeAdapter.refresh(false);
}
@@ -165,11 +152,11 @@ public class AppListActivity extends BaseActivity {
}
@Override
- public boolean onCreateOptionsMenu(@NonNull Menu menu) {
- scopeAdapter.onCreateOptionsMenu(menu, getMenuInflater());
+ public void onPrepareOptionsMenu(@NonNull Menu menu) {
+ super.onPrepareOptionsMenu(menu);
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setOnQueryTextListener(searchListener);
- return super.onCreateOptionsMenu(menu);
+ scopeAdapter.onPrepareOptionsMenu(menu);
}
@Override
@@ -180,17 +167,6 @@ public class AppListActivity extends BaseActivity {
return super.onContextItemSelected(item);
}
- @Override
- public void onBackPressed() {
- if (searchView.isIconified()) {
- if (scopeAdapter.onBackPressed()) {
- super.onBackPressed();
- }
- } else {
- searchView.setIconified(true);
- }
- }
-
public void makeSnackBar(String text, @Snackbar.Duration int duration) {
Snackbar.make(binding.snackbar, text, duration).show();
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/BaseFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/BaseFragment.java
new file mode 100644
index 00000000..46798e50
--- /dev/null
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/BaseFragment.java
@@ -0,0 +1,58 @@
+package org.lsposed.manager.ui.fragment;
+
+import android.view.View;
+
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.navigation.NavOptions;
+import androidx.navigation.Navigation;
+
+import org.lsposed.manager.R;
+
+public class BaseFragment extends Fragment {
+ public void navigateUp() {
+ getNavController().navigateUp();
+ }
+
+ public NavController getNavController() {
+ View view = getView();
+ if (view == null) {
+ return null;
+ }
+ View tabletFragmentContainer = view.findViewById(R.id.tablet_nav_container);
+ if (tabletFragmentContainer != null) {
+ return Navigation.findNavController(tabletFragmentContainer);
+ } else {
+ return Navigation.findNavController(view);
+ }
+ }
+
+ public void setupToolbar(Toolbar toolbar, int title) {
+ setupToolbar(toolbar, getString(title), -1);
+ }
+
+ public void setupToolbar(Toolbar toolbar, int title, int menu) {
+ setupToolbar(toolbar, getString(title), menu);
+ }
+
+ public void setupToolbar(Toolbar toolbar, String title, int menu) {
+ toolbar.setNavigationOnClickListener(v -> navigateUp());
+ toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24);
+ toolbar.setTitle(title);
+ if (menu != -1) {
+ toolbar.inflateMenu(menu);
+ toolbar.setOnMenuItemClickListener(this::onOptionsItemSelected);
+ onPrepareOptionsMenu(toolbar.getMenu());
+ }
+ }
+
+ public NavOptions getNavOptions() {
+ return new NavOptions.Builder()
+ .setEnterAnim(R.anim.nav_default_enter_anim)
+ .setExitAnim(R.anim.nav_default_exit_anim)
+ .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
+ .setPopExitAnim(R.anim.nav_default_pop_exit_anim)
+ .build();
+ }
+}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/CompileDialogFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/CompileDialogFragment.java
index 9c6b820f..219ffe8e 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/CompileDialogFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/CompileDialogFragment.java
@@ -41,7 +41,6 @@ import com.google.android.material.snackbar.Snackbar;
import org.lsposed.manager.App;
import org.lsposed.manager.R;
import org.lsposed.manager.databinding.FragmentCompileDialogBinding;
-import org.lsposed.manager.ui.activity.AppListActivity;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -156,9 +155,9 @@ public class CompileDialogFragment extends AppCompatDialogFragment {
CompileDialogFragment fragment = outerRef.get();
if (fragment != null) {
fragment.dismissAllowingStateLoss();
- AppListActivity activity = (AppListActivity) fragment.getActivity();
- if (activity != null && activity.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
- activity.makeSnackBar(text, Snackbar.LENGTH_LONG);
+ AppListFragment appListFragment = (AppListFragment) fragment.getParentFragment();
+ if (appListFragment != null && appListFragment.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
+ appListFragment.makeSnackBar(text, Snackbar.LENGTH_LONG);
return;
}
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java
new file mode 100644
index 00000000..cc499de0
--- /dev/null
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java
@@ -0,0 +1,154 @@
+package org.lsposed.manager.ui.fragment;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.text.HtmlCompat;
+import androidx.navigation.NavOptions;
+
+import com.bumptech.glide.Glide;
+import com.google.android.material.snackbar.Snackbar;
+
+import org.lsposed.manager.BuildConfig;
+import org.lsposed.manager.ConfigManager;
+import org.lsposed.manager.R;
+import org.lsposed.manager.databinding.DialogAboutBinding;
+import org.lsposed.manager.databinding.FragmentHomeBinding;
+import org.lsposed.manager.databinding.FragmentMainBinding;
+import org.lsposed.manager.ui.activity.base.BaseActivity;
+import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;
+import org.lsposed.manager.ui.dialog.InfoDialogBuilder;
+import org.lsposed.manager.util.GlideHelper;
+import org.lsposed.manager.util.ModuleUtil;
+import org.lsposed.manager.util.NavUtil;
+import org.lsposed.manager.util.chrome.LinkTransformationMethod;
+
+import java.util.Locale;
+
+import rikka.core.res.ResourcesKt;
+
+public class HomeFragment extends BaseFragment {
+
+ private FragmentHomeBinding binding;
+ private View snackbar;
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ FragmentMainBinding mainBinding = FragmentMainBinding.inflate(inflater, container, false);
+ snackbar = mainBinding.snackbar;
+ binding = FragmentHomeBinding.bind(mainBinding.snackbar);
+ return mainBinding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ BaseActivity activity = (BaseActivity) requireActivity();
+ binding.status.setOnClickListener(v -> {
+ if (ConfigManager.getXposedApiVersion() != -1) {
+ new InfoDialogBuilder(activity)
+ .setTitle(R.string.info)
+ .show();
+ } else {
+ NavUtil.startURL(activity, getString(R.string.about_source));
+ }
+ });
+ binding.modules.setOnClickListener(new StartFragmentListener(R.id.action_main_fragment_to_modules_fragment, true));
+ binding.download.setOnClickListener(new StartFragmentListener(R.id.action_main_fragment_to_repo_fragment, false));
+ binding.logs.setOnClickListener(new StartFragmentListener(R.id.action_main_fragment_to_logs_fragment, true));
+ binding.settings.setOnClickListener(new StartFragmentListener(R.id.action_main_fragment_to_settings_fragment, false));
+ binding.about.setOnClickListener(v -> {
+ DialogAboutBinding binding = DialogAboutBinding.inflate(LayoutInflater.from(requireActivity()), null, false);
+ binding.sourceCode.setMovementMethod(LinkMovementMethod.getInstance());
+ binding.sourceCode.setTransformationMethod(new LinkTransformationMethod(activity));
+ binding.sourceCode.setText(HtmlCompat.fromHtml(getString(
+ R.string.about_view_source_code,
+ "GitHub",
+ "Telegram"), HtmlCompat.FROM_HTML_MODE_LEGACY));
+ binding.translators.setMovementMethod(LinkMovementMethod.getInstance());
+ binding.translators.setTransformationMethod(new LinkTransformationMethod(activity));
+ binding.translators.setText(HtmlCompat.fromHtml(getString(R.string.about_translators, getString(R.string.translators)), HtmlCompat.FROM_HTML_MODE_LEGACY));
+ binding.version.setText(String.format(Locale.US, "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
+ new BlurBehindDialogBuilder(activity)
+ .setView(binding.getRoot())
+ .show();
+ });
+ Glide.with(binding.appIcon)
+ .load(GlideHelper.wrapApplicationInfoForIconLoader(activity.getApplicationInfo()))
+ .into(binding.appIcon);
+ String installXposedVersion = ConfigManager.getXposedVersionName();
+ int cardBackgroundColor;
+ if (installXposedVersion != null) {
+ if (!ConfigManager.isSepolicyLoaded()) {
+ binding.statusTitle.setText(R.string.partial_activated);
+ cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorWarning);
+ binding.statusIcon.setImageResource(R.drawable.ic_warning);
+ binding.statusSummary.setText(R.string.selinux_policy_not_loaded_summary);
+ } else if (!ConfigManager.systemServerRequested()) {
+ binding.statusTitle.setText(R.string.partial_activated);
+ cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorWarning);
+ binding.statusIcon.setImageResource(R.drawable.ic_warning);
+ binding.statusSummary.setText(R.string.system_inject_fail_summary);
+ } else {
+ binding.statusTitle.setText(R.string.activated);
+ cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorNormal);
+ binding.statusIcon.setImageResource(R.drawable.ic_check_circle);
+ binding.statusSummary.setText(String.format(Locale.US, "%s (%d)", installXposedVersion, ConfigManager.getXposedVersionCode()));
+ }
+ } else {
+ cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorInstall);
+ boolean isMagiskInstalled = ConfigManager.isMagiskInstalled();
+ binding.statusTitle.setText(isMagiskInstalled ? R.string.Install : R.string.NotInstall);
+ binding.statusSummary.setText(isMagiskInstalled ? R.string.InstallDetail : R.string.NotInstallDetail);
+ if (!isMagiskInstalled) {
+ binding.status.setOnClickListener(null);
+ binding.download.setVisibility(View.GONE);
+ }
+ binding.statusIcon.setImageResource(R.drawable.ic_error);
+ Snackbar.make(snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ binding.status.setCardBackgroundColor(cardBackgroundColor);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ binding.status.setOutlineSpotShadowColor(cardBackgroundColor);
+ binding.status.setOutlineAmbientShadowColor(cardBackgroundColor);
+ }
+ }
+
+ private class StartFragmentListener implements View.OnClickListener {
+ boolean requireInstalled;
+ int fragment;
+
+ StartFragmentListener(int fragment, boolean requireInstalled) {
+ this.fragment = fragment;
+ this.requireInstalled = requireInstalled;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (requireInstalled && ConfigManager.getXposedVersionName() == null) {
+ Snackbar.make(snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show();
+ } else {
+ getNavController().navigate(fragment, null, getNavOptions());
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ int moduleCount = ModuleUtil.getInstance().getEnabledModulesCount();
+ binding.modulesSummary.setText(getResources().getQuantityString(R.plurals.modules_enabled_count, moduleCount, moduleCount));
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ binding = null;
+ }
+}
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
similarity index 83%
rename from app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java
rename to app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
index 9bac020e..3f52630a 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/LogsActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
@@ -18,7 +18,7 @@
* Copyright (C) 2021 LSPosed Contributors
*/
-package org.lsposed.manager.ui.activity;
+package org.lsposed.manager.ui.fragment;
import android.annotation.SuppressLint;
import android.content.Intent;
@@ -29,7 +29,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +38,6 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.RecyclerView;
@@ -47,13 +45,13 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
+import org.lsposed.manager.App;
import org.lsposed.manager.BuildConfig;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
-import org.lsposed.manager.databinding.ActivityLogsBinding;
import org.lsposed.manager.databinding.DialogInstallWarningBinding;
+import org.lsposed.manager.databinding.FragmentLogsBinding;
import org.lsposed.manager.databinding.ItemLogBinding;
-import org.lsposed.manager.ui.activity.base.BaseActivity;
import org.lsposed.manager.util.LinearLayoutManagerFix;
import java.io.BufferedReader;
@@ -76,23 +74,23 @@ import rikka.insets.WindowInsetsHelperKt;
import rikka.recyclerview.RecyclerViewKt;
@SuppressLint("NotifyDataSetChanged")
-public class LogsActivity extends BaseActivity {
+public class LogsFragment extends BaseFragment {
private boolean verbose = false;
private LogsAdapter adapter;
private final Handler handler = new Handler(Looper.getMainLooper());
- private ActivityLogsBinding binding;
+ private FragmentLogsBinding binding;
private LinearLayoutManagerFix layoutManager;
ActivityResultLauncher saveLogsLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
uri -> {
if (uri != null) {
try {
// grantUriPermission might throw RemoteException on MIUI
- grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ requireContext().grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} catch (Exception e) {
e.printStackTrace();
}
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- try (var os = getContentResolver().openOutputStream(uri)) {
+ try (var os = requireContext().getContentResolver().openOutputStream(uri)) {
ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLogs(verbose);
if (parcelFileDescriptor == null) {
return;
@@ -107,59 +105,65 @@ public class LogsActivity extends BaseActivity {
}
});
+ @Nullable
@Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding = ActivityLogsBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
- setAppBar(binding.appBar, binding.toolbar);
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentLogsBinding.inflate(inflater, container, false);
binding.getRoot().bringChildToFront(binding.appBar);
- binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
+ setupToolbar(binding.toolbar, R.string.Logs, R.menu.menu_logs);
binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
- ActionBar bar = getSupportActionBar();
- if (bar != null) {
- bar.setDisplayHomeAsUpEnabled(true);
+
+
+ if (!ConfigManager.isVerboseLogEnabled()) {
+ WindowInsetsHelperKt.setInitialPadding(binding.recyclerView, 0, ResourcesKt.resolveDimensionPixelOffset(requireActivity().getTheme(), R.attr.actionBarSize, 0), 0, 0);
+ binding.slidingTabs.setVisibility(View.GONE);
+ } else {
+ binding.slidingTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ verbose = tab.getPosition() == 1;
+ reloadErrorLog();
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
}
- if (!preferences.getBoolean("hide_logcat_warning", false)) {
+
+ adapter = new LogsAdapter();
+ RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
+ binding.recyclerView.setAdapter(adapter);
+ layoutManager = new LinearLayoutManagerFix(requireActivity());
+ binding.recyclerView.setLayoutManager(layoutManager);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ if (!App.getPreferences().getBoolean("hide_logcat_warning", false)) {
DialogInstallWarningBinding binding = DialogInstallWarningBinding.inflate(getLayoutInflater());
binding.getRoot().setOnClickListener(v -> binding.checkbox.toggle());
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(requireActivity())
.setMessage(R.string.not_logcat_2)
.setView(binding.getRoot())
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
if (binding.checkbox.isChecked()) {
- preferences.edit().putBoolean("hide_logcat_warning", true).apply();
+ App.getPreferences().edit().putBoolean("hide_logcat_warning", true).apply();
}
})
.setCancelable(false)
.show();
}
- if (!ConfigManager.isVerboseLogEnabled()) {
- WindowInsetsHelperKt.setInitialPadding(binding.recyclerView, 0, ResourcesKt.resolveDimensionPixelOffset(getTheme(), R.attr.actionBarSize, 0), 0, 0);
- binding.slidingTabs.setVisibility(View.GONE);
- }
- adapter = new LogsAdapter();
- RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
- binding.recyclerView.setAdapter(adapter);
- layoutManager = new LinearLayoutManagerFix(this);
- binding.recyclerView.setLayoutManager(layoutManager);
- binding.slidingTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
- @Override
- public void onTabSelected(TabLayout.Tab tab) {
- verbose = tab.getPosition() == 1;
- reloadErrorLog();
- }
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {
-
- }
-
- @Override
- public void onTabReselected(TabLayout.Tab tab) {
-
- }
- });
}
@Override
@@ -168,12 +172,6 @@ public class LogsActivity extends BaseActivity {
reloadErrorLog();
}
- @Override
- public boolean onCreateOptionsMenu(@NonNull Menu menu) {
- getMenuInflater().inflate(R.menu.menu_logs, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
@@ -216,7 +214,7 @@ public class LogsActivity extends BaseActivity {
new LogsReader().execute(parcelFileDescriptor.getFileDescriptor());
} else {
binding.slidingTabs.selectTab(binding.slidingTabs.getTabAt(0));
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(requireActivity())
.setMessage(R.string.verbose_log_not_avaliable)
.setPositiveButton(android.R.string.ok, null)
.show();
@@ -244,7 +242,7 @@ public class LogsActivity extends BaseActivity {
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 cacheFile = new File(getCacheDir(), filename);
+ File cacheFile = new File(requireActivity().getCacheDir(), filename);
try (var os = new FileOutputStream(cacheFile); var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) {
FileUtils.copy(is, os);
} catch (IOException e) {
@@ -252,7 +250,7 @@ public class LogsActivity extends BaseActivity {
return;
}
- Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", cacheFile);
+ Uri uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", cacheFile);
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
@@ -279,7 +277,7 @@ public class LogsActivity extends BaseActivity {
private final Runnable mRunnable = new Runnable() {
@Override
public void run() {
- if (!isFinishing()) {
+ if (!requireActivity().isFinishing()) {
mProgressDialog.show();
}
}
@@ -287,7 +285,7 @@ public class LogsActivity extends BaseActivity {
@Override
protected void onPreExecute() {
- mProgressDialog = new AlertDialog.Builder(LogsActivity.this).create();
+ mProgressDialog = new AlertDialog.Builder(requireActivity()).create();
mProgressDialog.setMessage(getString(R.string.loading));
mProgressDialog.setCancelable(false);
handler.postDelayed(mRunnable, 300);
@@ -305,7 +303,7 @@ public class LogsActivity extends BaseActivity {
logs.add(line);
}
} catch (IOException e) {
- logs.add(LogsActivity.this.getResources().getString(R.string.logs_cannot_read));
+ logs.add(requireActivity().getResources().getString(R.string.logs_cannot_read));
if (e.getMessage() != null) {
logs.addAll(Arrays.asList(e.getMessage().split("\n")));
}
@@ -368,6 +366,4 @@ public class LogsActivity extends BaseActivity {
}
}
}
-
-
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
similarity index 84%
rename from app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java
rename to app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
index 8a7e7e24..746509d7 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
@@ -1,24 +1,4 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
-
-package org.lsposed.manager.ui.activity;
+package org.lsposed.manager.ui.fragment;
import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
@@ -39,6 +19,7 @@ import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -51,7 +32,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -70,12 +50,11 @@ import org.lsposed.lspd.models.UserInfo;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.adapters.AppHelper;
-import org.lsposed.manager.databinding.ActivityModuleDetailBinding;
import org.lsposed.manager.databinding.DialogRecyclerviewBinding;
+import org.lsposed.manager.databinding.FragmentPagerBinding;
import org.lsposed.manager.databinding.ItemModuleBinding;
import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding;
import org.lsposed.manager.repo.RepoLoader;
-import org.lsposed.manager.ui.activity.base.BaseActivity;
import org.lsposed.manager.ui.widget.EmptyStateRecyclerView;
import org.lsposed.manager.util.GlideApp;
import org.lsposed.manager.util.LinearLayoutManagerFix;
@@ -93,13 +72,14 @@ import rikka.insets.WindowInsetsHelperKt;
import rikka.recyclerview.RecyclerViewKt;
import rikka.widget.borderview.BorderRecyclerView;
-public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleListener {
+public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleListener {
- protected ActivityModuleDetailBinding binding;
+ protected FragmentPagerBinding binding;
protected SearchView searchView;
private SearchView.OnQueryTextListener mSearchListener;
private final PagerAdapter pagerAdapter = new PagerAdapter();
private final ArrayList adapters = new ArrayList<>();
+ private final ArrayList titles = new ArrayList<>();
private Handler workHandler;
private PackageManager pm;
@@ -107,23 +87,36 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
private ModuleUtil.InstalledModule selectedModule;
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
HandlerThread workThread = new HandlerThread("ModulesActivity WorkHandler");
workThread.start();
workHandler = new Handler(workThread.getLooper());
moduleUtil = ModuleUtil.getInstance();
- pm = getPackageManager();
+ pm = requireContext().getPackageManager();
moduleUtil.addListener(this);
- super.onCreate(savedInstanceState);
- binding = ActivityModuleDetailBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
- setAppBar(binding.appBar, binding.toolbar);
+ mSearchListener = new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ adapters.forEach(adapter -> adapter.getFilter().filter(query));
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ adapters.forEach(adapter -> adapter.getFilter().filter(newText));
+ return false;
+ }
+ };
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentPagerBinding.inflate(inflater, container, false);
+
binding.getRoot().bringChildToFront(binding.appBar);
- binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
- ActionBar bar = getSupportActionBar();
- if (bar != null) {
- bar.setDisplayHomeAsUpEnabled(true);
- }
+ setupToolbar(binding.toolbar, R.string.Modules, R.menu.menu_modules);
binding.viewPager.setAdapter(new PagerAdapter());
binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
@@ -138,19 +131,19 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
}
});
- binding.fab.setOnClickListener(view -> {
+ binding.fab.setOnClickListener(v -> {
var pickAdaptor = new ModuleAdapter(null, true);
var position = binding.viewPager.getCurrentItem();
var snapshot = adapters.get(position).snapshot().stream().map(m -> m.packageName).collect(Collectors.toSet());
var user = adapters.get(position).getUser();
pickAdaptor.setFilter(m -> !snapshot.contains(m.packageName));
pickAdaptor.refresh();
- var v = DialogRecyclerviewBinding.inflate(getLayoutInflater()).getRoot();
- v.setAdapter(pickAdaptor);
- v.setLayoutManager(new LinearLayoutManagerFix(ModulesActivity.this));
- var dialog = new AlertDialog.Builder(ModulesActivity.this)
+ var rv = DialogRecyclerviewBinding.inflate(getLayoutInflater()).getRoot();
+ rv.setAdapter(pickAdaptor);
+ rv.setLayoutManager(new LinearLayoutManagerFix(requireActivity()));
+ var dialog = new AlertDialog.Builder(requireActivity())
.setTitle(getString(R.string.install_to_user, user.name))
- .setView(v)
+ .setView(rv)
.setNegativeButton(android.R.string.cancel, null)
.show();
pickAdaptor.setOnPickListener(picked -> {
@@ -160,43 +153,26 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
});
});
- mSearchListener = new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- adapters.forEach(adapter -> adapter.getFilter().filter(query));
- return false;
- }
+ return binding.getRoot();
+ }
- @Override
- public boolean onQueryTextChange(String newText) {
- adapters.forEach(adapter -> adapter.getFilter().filter(newText));
- return false;
- }
- };
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
if (ConfigManager.getXposedVersionName() == null) {
- Toast.makeText(this, R.string.lsposed_not_active, Toast.LENGTH_LONG).show();
- finish();
+ Toast.makeText(requireContext(), R.string.lsposed_not_active, Toast.LENGTH_LONG).show();
+ getNavController().navigateUp();
}
}
@Override
- public boolean onPrepareOptionsMenu(Menu menu) {
+ public void onPrepareOptionsMenu(Menu menu) {
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setOnQueryTextListener(mSearchListener);
- return super.onPrepareOptionsMenu(menu);
}
@Override
- public void onBackPressed() {
- if (searchView.isIconified()) {
- super.onBackPressed();
- } else {
- searchView.setIconified(true);
- }
- }
-
- @Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
var users = ConfigManager.getUsers();
if (users != null) {
@@ -204,14 +180,12 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
adapters.clear();
if (users.size() != 1) {
binding.viewPager.setUserInputEnabled(true);
- ArrayList titles = new ArrayList<>();
for (var user : users) {
var adapter = new ModuleAdapter(user);
adapter.setHasStableIds(true);
adapters.add(adapter);
titles.add(user.name);
}
- new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> tab.setText(titles.get(position))).attach();
binding.tabLayout.setVisibility(View.VISIBLE);
} else {
binding.viewPager.setUserInputEnabled(false);
@@ -222,16 +196,13 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
}
pagerAdapter.notifyDataSetChanged();
}
+ if (users.size() != 1) {
+ new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> tab.setText(titles.get(position))).attach();
+ }
}
adapters.forEach(ModuleAdapter::refresh);
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_modules, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
@Override
public void onDestroy() {
super.onDestroy();
@@ -253,18 +224,18 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
}
private void installModuleToUser(ModuleUtil.InstalledModule module, UserInfo user) {
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(requireActivity())
.setTitle(getString(R.string.install_to_user, user.name))
.setMessage(getString(R.string.install_to_user_message, module.getAppName(), user.name))
.setPositiveButton(android.R.string.ok, (dialog, which) ->
workHandler.post(() -> {
var success = ConfigManager.installExistingPackageAsUser(module.packageName, user.id);
- runOnUiThread(() -> {
+ requireActivity().runOnUiThread(() -> {
String text = success ? getString(R.string.module_installed, module.getAppName(), user.name) : getString(R.string.module_install_failed);
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
Snackbar.make(binding.snackbar, text, Snackbar.LENGTH_SHORT).show();
} else {
- Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show();
+ Toast.makeText(requireActivity(), text, Toast.LENGTH_SHORT).show();
}
});
if (success)
@@ -302,18 +273,18 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
ConfigManager.startActivityAsUserWithFeature(new Intent(ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", selectedModule.packageName, null)), selectedModule.userId);
return true;
} else if (itemId == R.id.menu_uninstall) {
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(requireActivity())
.setTitle(selectedModule.getAppName())
.setMessage(R.string.module_uninstall_message)
.setPositiveButton(android.R.string.ok, (dialog, which) ->
workHandler.post(() -> {
boolean success = ConfigManager.uninstallPackage(selectedModule.packageName, selectedModule.userId);
- runOnUiThread(() -> {
+ requireActivity().runOnUiThread(() -> {
String text = success ? getString(R.string.module_uninstalled, selectedModule.getAppName()) : getString(R.string.module_uninstall_failed);
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
Snackbar.make(binding.snackbar, text, Snackbar.LENGTH_SHORT).show();
} else {
- Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show();
+ Toast.makeText(requireActivity(), text, Toast.LENGTH_SHORT).show();
}
});
if (success)
@@ -323,11 +294,10 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
.show();
return true;
} else if (itemId == R.id.menu_repo) {
- Intent intent = new Intent();
- intent.setClass(this, RepoItemActivity.class);
- intent.putExtra("modulePackageName", selectedModule.packageName);
- intent.putExtra("moduleName", selectedModule.getAppName());
- startActivity(intent);
+ Bundle bundle = new Bundle();
+ bundle.putString("modulePackageName", selectedModule.packageName);
+ bundle.putString("moduleName", selectedModule.getAppName());
+ getNavController().navigate(R.id.action_modules_fragment_to_repo_item_fragment, bundle, getNavOptions());
return true;
}
return super.onContextItemSelected(item);
@@ -344,11 +314,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@Override
public void onBindViewHolder(@NonNull PagerAdapter.ViewHolder holder, int position) {
if (getItemCount() == 1) {
- WindowInsetsHelperKt.setInitialPadding(holder.recyclerView, 0, ResourcesKt.resolveDimensionPixelOffset(getTheme(), R.attr.actionBarSize, 0), 0, 0);
+ WindowInsetsHelperKt.setInitialPadding(holder.recyclerView, 0, ResourcesKt.resolveDimensionPixelOffset(requireActivity().getTheme(), R.attr.actionBarSize, 0), 0, 0);
}
holder.recyclerView.setTag(position);
holder.recyclerView.setAdapter(adapters.get(position));
- holder.recyclerView.setLayoutManager(new LinearLayoutManagerFix(ModulesActivity.this));
+ holder.recyclerView.setLayoutManager(new LinearLayoutManagerFix(requireActivity()));
holder.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
holder.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
@@ -403,12 +373,12 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@NonNull
@Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new ViewHolder(ItemModuleBinding.inflate(getLayoutInflater(), parent, false));
+ public ModuleAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ModuleAdapter.ViewHolder(ItemModuleBinding.inflate(getLayoutInflater(), parent, false));
}
@Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull ModuleAdapter.ViewHolder holder, int position) {
ModuleUtil.InstalledModule item = showList.get(position);
String appName;
if (item.userId != 0) {
@@ -451,7 +421,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (warningText != null) {
sb.append("\n");
sb.append(warningText);
- final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ContextCompat.getColor(ModulesActivity.this, R.color.material_red_500));
+ final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ContextCompat.getColor(requireActivity(), R.color.material_red_500));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create("sans-serif-medium", Typeface.NORMAL));
sb.setSpan(typefaceSpan, sb.length() - warningText.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
@@ -466,17 +436,17 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (!isPick) {
holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f);
holder.itemView.setOnClickListener(v -> {
- Intent intent = new Intent(ModulesActivity.this, AppListActivity.class);
- intent.putExtra("modulePackageName", item.packageName);
- intent.putExtra("moduleUserId", item.userId);
- startActivity(intent);
+ Bundle bundle = new Bundle();
+ bundle.putString("modulePackageName", item.packageName);
+ bundle.putInt("moduleUserId", item.userId);
+ getNavController().navigate(R.id.action_modules_fragment_to_app_list_fragment, bundle, getNavOptions());
});
holder.itemView.setOnLongClickListener(v -> {
selectedModule = item;
return false;
});
holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> {
- getMenuInflater().inflate(R.menu.context_menu_modules, menu);
+ requireActivity().getMenuInflater().inflate(R.menu.context_menu_modules, menu);
menu.setHeaderTitle(item.getAppName());
Intent intent = AppHelper.getSettingsIntent(item.packageName, item.userId);
if (intent == null) {
@@ -523,7 +493,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@Override
public Filter getFilter() {
- return new ApplicationFilter();
+ return new ModuleAdapter.ApplicationFilter();
}
public void setFilter(@NonNull Predicate filter) {
@@ -544,7 +514,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
public void refresh(boolean force) {
if (force) moduleUtil.reloadInstalledModules();
- runOnUiThread(reloadModules);
+ requireActivity().runOnUiThread(reloadModules);
}
private final Runnable reloadModules = new Runnable() {
@@ -565,7 +535,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
searchList.clear();
searchList.addAll(tmpList);
String queryStr = searchView != null ? searchView.getQuery().toString() : "";
- runOnUiThread(() -> getFilter().filter(queryStr));
+ requireActivity().runOnUiThread(() -> getFilter().filter(queryStr));
}
};
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/RepoActivity.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
similarity index 63%
rename from app/src/main/java/org/lsposed/manager/ui/activity/RepoActivity.java
rename to app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
index c15b97aa..4ba17c32 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/RepoActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
@@ -1,50 +1,35 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
+package org.lsposed.manager.ui.fragment;
-package org.lsposed.manager.ui.activity;
-
-import android.content.Intent;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
+import android.widget.Filterable;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
+import org.lsposed.manager.App;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
+import org.lsposed.manager.databinding.FragmentRepoBinding;
import org.lsposed.manager.databinding.ItemOnlinemoduleBinding;
import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.repo.model.OnlineModule;
-import org.lsposed.manager.ui.activity.base.ListActivity;
+import org.lsposed.manager.util.LinearLayoutManagerFix;
import java.time.Instant;
import java.util.ArrayList;
@@ -55,41 +40,89 @@ import java.util.List;
import java.util.stream.Collectors;
import rikka.core.util.LabelComparator;
+import rikka.recyclerview.RecyclerViewKt;
+
+public class RepoFragment extends BaseFragment implements RepoLoader.Listener {
+ protected FragmentRepoBinding binding;
+ protected SearchView searchView;
+ private SearchView.OnQueryTextListener mSearchListener;
-public class RepoActivity extends ListActivity implements RepoLoader.Listener {
private final RepoLoader repoLoader = RepoLoader.getInstance();
private RepoAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
repoLoader.addListener(this);
+ mSearchListener = new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ adapter.getFilter().filter(query);
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ adapter.getFilter().filter(newText);
+ return false;
+ }
+ };
super.onCreate(savedInstanceState);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentRepoBinding.inflate(getLayoutInflater(), container, false);
+ binding.getRoot().bringChildToFront(binding.appBar);
+ setupToolbar(binding.toolbar, R.string.module_repo, R.menu.menu_repo);
+ binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
+ adapter = new RepoAdapter();
+ adapter.setHasStableIds(true);
+ binding.recyclerView.setAdapter(adapter);
+ binding.recyclerView.setHasFixedSize(true);
+ binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(requireActivity()));
+ RecyclerViewKt.addFastScroller(binding.recyclerView, binding.recyclerView);
+ RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
+ binding.progress.setVisibilityAfterHide(View.GONE);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
if (ConfigManager.getXposedVersionName() == null && !ConfigManager.isMagiskInstalled()) {
- Toast.makeText(this, R.string.lsposed_not_active, Toast.LENGTH_LONG).show();
- finish();
+ Toast.makeText(requireActivity(), R.string.lsposed_not_active, Toast.LENGTH_LONG).show();
+ getNavController().navigateUp();
}
}
@Override
- protected BaseAdapter> createAdapter() {
- return adapter = new RepoAdapter();
+ public void onPrepareOptionsMenu(Menu menu) {
+ searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
+ searchView.setOnQueryTextListener(mSearchListener);
+ int sort = App.getPreferences().getInt("repo_sort", 0);
+ if (sort == 0) {
+ menu.findItem(R.id.item_sort_by_name).setChecked(true);
+ } else if (sort == 1) {
+ menu.findItem(R.id.item_sort_by_update_time).setChecked(true);
+ }
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
super.onDestroy();
repoLoader.removeListener(this);
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
adapter.initData();
}
@Override
public void repoLoaded() {
- runOnUiThread(() -> {
+ requireActivity().runOnUiThread(() -> {
binding.progress.hide();
adapter.setData(repoLoader.getOnlineModules());
});
@@ -115,29 +148,17 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
repoLoader.loadRemoteData();
} else if (itemId == R.id.item_sort_by_name) {
item.setChecked(true);
- preferences.edit().putInt("repo_sort", 0).apply();
+ App.getPreferences().edit().putInt("repo_sort", 0).apply();
adapter.setData(repoLoader.getOnlineModules());
} else if (itemId == R.id.item_sort_by_update_time) {
item.setChecked(true);
- preferences.edit().putInt("repo_sort", 1).apply();
+ App.getPreferences().edit().putInt("repo_sort", 1).apply();
adapter.setData(repoLoader.getOnlineModules());
}
return super.onOptionsItemSelected(item);
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_repo, menu);
- int sort = preferences.getInt("repo_sort", 0);
- if (sort == 0) {
- menu.findItem(R.id.item_sort_by_name).setChecked(true);
- } else if (sort == 1) {
- menu.findItem(R.id.item_sort_by_update_time).setChecked(true);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- private class RepoAdapter extends BaseAdapter {
+ private class RepoAdapter extends RecyclerView.Adapter implements Filterable {
private List fullList, showList;
private final LabelComparator labelComparator = new LabelComparator();
@@ -147,12 +168,12 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
@NonNull
@Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new ViewHolder(ItemOnlinemoduleBinding.inflate(getLayoutInflater(), parent, false));
+ public RepoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new RepoAdapter.ViewHolder(ItemOnlinemoduleBinding.inflate(getLayoutInflater(), parent, false));
}
@Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull RepoAdapter.ViewHolder holder, int position) {
OnlineModule module = showList.get(position);
holder.appName.setText(module.getDescription());
SpannableStringBuilder sb = new SpannableStringBuilder(module.getName());
@@ -163,11 +184,10 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
}
holder.appDescription.setText(sb);
holder.itemView.setOnClickListener(v -> {
- Intent intent = new Intent();
- intent.setClass(RepoActivity.this, RepoItemActivity.class);
- intent.putExtra("modulePackageName", module.getName());
- intent.putExtra("moduleName", module.getDescription());
- startActivity(intent);
+ Bundle bundle = new Bundle();
+ bundle.putString("modulePackageName", module.getName());
+ bundle.putString("moduleName", module.getDescription());
+ getNavController().navigate(R.id.action_repo_fragment_to_repo_item_fragment, bundle, getNavOptions());
});
}
@@ -179,14 +199,14 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
public void setData(Collection modules) {
fullList = new ArrayList<>(modules);
fullList = fullList.stream().filter((onlineModule -> !onlineModule.isHide())).collect(Collectors.toList());
- int sort = preferences.getInt("repo_sort", 0);
+ int sort = App.getPreferences().getInt("repo_sort", 0);
if (sort == 0) {
fullList.sort((o1, o2) -> labelComparator.compare(o1.getDescription(), o2.getDescription()));
} else if (sort == 1) {
fullList.sort(Collections.reverseOrder(Comparator.comparing(o -> Instant.parse(o.getReleases().get(0).getUpdatedAt()))));
}
String queryStr = searchView != null ? searchView.getQuery().toString() : "";
- runOnUiThread(() -> getFilter().filter(queryStr));
+ requireActivity().runOnUiThread(() -> getFilter().filter(queryStr));
}
public void initData() {
@@ -206,7 +226,7 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
@Override
public Filter getFilter() {
- return new ModuleFilter();
+ return new RepoAdapter.ModuleFilter();
}
class ViewHolder extends RecyclerView.ViewHolder {
diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/RepoItemActivity.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
similarity index 79%
rename from app/src/main/java/org/lsposed/manager/ui/activity/RepoItemActivity.java
rename to app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
index 4c4ed760..5161a973 100644
--- a/app/src/main/java/org/lsposed/manager/ui/activity/RepoItemActivity.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
@@ -1,24 +1,4 @@
-/*
- * This file is part of LSPosed.
- *
- * LSPosed is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LSPosed is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LSPosed. If not, see .
- *
- * Copyright (C) 2020 EdXposed Contributors
- * Copyright (C) 2021 LSPosed Contributors
- */
-
-package org.lsposed.manager.ui.activity;
+package org.lsposed.manager.ui.fragment;
import android.os.Bundle;
import android.text.Spannable;
@@ -26,7 +6,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.text.util.Linkify;
-import android.view.Menu;
+import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -34,7 +14,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
@@ -46,7 +25,7 @@ import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayoutMediator;
import org.lsposed.manager.R;
-import org.lsposed.manager.databinding.ActivityModuleDetailBinding;
+import org.lsposed.manager.databinding.FragmentPagerBinding;
import org.lsposed.manager.databinding.ItemRepoLoadmoreBinding;
import org.lsposed.manager.databinding.ItemRepoReadmeBinding;
import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding;
@@ -57,7 +36,6 @@ import org.lsposed.manager.repo.model.Collaborator;
import org.lsposed.manager.repo.model.OnlineModule;
import org.lsposed.manager.repo.model.Release;
import org.lsposed.manager.repo.model.ReleaseAsset;
-import org.lsposed.manager.ui.activity.base.BaseActivity;
import org.lsposed.manager.ui.widget.LinkifyTextView;
import org.lsposed.manager.util.GlideApp;
import org.lsposed.manager.util.LinearLayoutManagerFix;
@@ -83,38 +61,21 @@ import rikka.widget.borderview.BorderNestedScrollView;
import rikka.widget.borderview.BorderRecyclerView;
import rikka.widget.borderview.BorderView;
-public class RepoItemActivity extends BaseActivity implements RepoLoader.Listener {
- ActivityModuleDetailBinding binding;
+public class RepoItemFragment extends BaseFragment implements RepoLoader.Listener {
+ FragmentPagerBinding binding;
private Markwon markwon;
private OnlineModule module;
private ReleaseAdapter releaseAdapter;
+ @Nullable
@Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- RepoLoader.getInstance().addListener(this);
- super.onCreate(savedInstanceState);
- binding = ActivityModuleDetailBinding.inflate(getLayoutInflater());
- String modulePackageName = getIntent().getStringExtra("modulePackageName");
- String moduleName = getIntent().getStringExtra("moduleName");
- setContentView(binding.getRoot());
- setAppBar(binding.appBar, binding.toolbar);
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentPagerBinding.inflate(getLayoutInflater(), container, false);
+ String modulePackageName = getArguments().getString("modulePackageName");
+ String moduleName = getArguments().getString("moduleName");
binding.getRoot().bringChildToFront(binding.appBar);
- binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
- ActionBar bar = getSupportActionBar();
- assert bar != null;
- bar.setTitle(moduleName);
- bar.setSubtitle(modulePackageName);
- bar.setDisplayHomeAsUpEnabled(true);
- markwon = Markwon.builder(this)
- .usePlugin(StrikethroughPlugin.create())
- .usePlugin(TablePlugin.create(this))
- .usePlugin(TaskListPlugin.create(this))
- .usePlugin(HtmlPlugin.create())
- .usePlugin(GlideImagesPlugin.create(GlideApp.with(this)))
- .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS, true))
- .usePlugin(SoftBreakAddsNewLinePlugin.create())
- .build();
- module = RepoLoader.getInstance().getOnlineModule(modulePackageName);
+ setupToolbar(binding.toolbar, moduleName, R.menu.menu_repo_item);
+ binding.toolbar.setSubtitle(modulePackageName);
binding.viewPager.setAdapter(new PagerAdapter());
binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
@@ -128,19 +89,38 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
});
int[] titles = new int[]{R.string.module_readme, R.string.module_releases, R.string.module_information};
new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> tab.setText(titles[position])).attach();
+ return binding.getRoot();
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_repo_item, menu);
- return super.onCreateOptionsMenu(menu);
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ RepoLoader.getInstance().addListener(this);
+ super.onCreate(savedInstanceState);
+
+ markwon = Markwon.builder(requireActivity())
+ .usePlugin(StrikethroughPlugin.create())
+ .usePlugin(TablePlugin.create(requireActivity()))
+ .usePlugin(TaskListPlugin.create(requireActivity()))
+ .usePlugin(HtmlPlugin.create())
+ .usePlugin(GlideImagesPlugin.create(GlideApp.with(this)))
+ .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS, true))
+ .usePlugin(SoftBreakAddsNewLinePlugin.create())
+ .build();
+ String modulePackageName = getArguments().getString("modulePackageName");
+ module = RepoLoader.getInstance().getOnlineModule(modulePackageName);
+
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_open_in_browser) {
- NavUtil.startURL(this, "https://modules.lsposed.org/module/" + module.getName());
+ NavUtil.startURL(requireActivity(), "https://modules.lsposed.org/module/" + module.getName());
}
return super.onOptionsItemSelected(item);
}
@@ -154,7 +134,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
public void moduleReleasesLoaded(OnlineModule module) {
this.module = module;
if (releaseAdapter != null) {
- runOnUiThread(() -> releaseAdapter.loadItems());
+ requireActivity().runOnUiThread(() -> releaseAdapter.loadItems());
if (module.getReleases().size() == 1) {
Snackbar.make(binding.snackbar, R.string.module_release_no_more, Snackbar.LENGTH_SHORT).show();
}
@@ -164,7 +144,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
@Override
public void onThrowable(Throwable t) {
if (releaseAdapter != null) {
- runOnUiThread(() -> releaseAdapter.loadItems());
+ requireActivity().runOnUiThread(() -> releaseAdapter.loadItems());
}
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
Snackbar.make(binding.snackbar, getString(R.string.repo_load_failed, t.getLocalizedMessage()), Snackbar.LENGTH_SHORT).show();
@@ -172,7 +152,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
super.onDestroy();
RepoLoader.getInstance().removeListener(this);
}
@@ -218,7 +198,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
Collaborator collaborator = iterator.next();
String name = collaborator.getName() == null ? collaborator.getLogin() : collaborator.getName();
sb.append(name);
- CustomTabsURLSpan span = new CustomTabsURLSpan(RepoItemActivity.this, String.format("https://github.com/%s", collaborator.getLogin()));
+ CustomTabsURLSpan span = new CustomTabsURLSpan(requireActivity(), String.format("https://github.com/%s", collaborator.getLogin()));
sb.setSpan(span, sb.length() - name.length(), sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (iterator.hasNext()) {
sb.append(", ");
@@ -231,7 +211,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
holder.itemView.setOnClickListener(v -> {
if (position == homepageRow) {
- NavUtil.startURL(RepoItemActivity.this, module.getHomepageUrl());
+ NavUtil.startURL(requireActivity(), module.getHomepageUrl());
} else if (position == collaboratorsRow) {
ClickableSpan span = holder.description.getCurrentSpan();
holder.description.clearCurrentSpan();
@@ -240,7 +220,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
span.onClick(v);
}
} else if (position == sourceUrlRow) {
- NavUtil.startURL(RepoItemActivity.this, module.getSourceUrl());
+ NavUtil.startURL(requireActivity(), module.getSourceUrl());
}
});
@@ -277,16 +257,16 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
@NonNull
@Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ public ReleaseAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
- return new ReleaseViewHolder(ItemRepoReleaseBinding.inflate(getLayoutInflater(), parent, false));
+ return new ReleaseAdapter.ReleaseViewHolder(ItemRepoReleaseBinding.inflate(getLayoutInflater(), parent, false));
} else {
- return new LoadmoreViewHolder(ItemRepoLoadmoreBinding.inflate(getLayoutInflater(), parent, false));
+ return new ReleaseAdapter.LoadmoreViewHolder(ItemRepoLoadmoreBinding.inflate(getLayoutInflater(), parent, false));
}
}
@Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull ReleaseAdapter.ViewHolder holder, int position) {
if (position == items.size()) {
holder.progress.setVisibility(View.GONE);
holder.title.setVisibility(View.VISIBLE);
@@ -300,18 +280,18 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
} else {
Release release = items.get(position);
holder.title.setText(release.getName());
- holder.description.setTransformationMethod(new LinkTransformationMethod(RepoItemActivity.this));
+ holder.description.setTransformationMethod(new LinkTransformationMethod(requireActivity()));
holder.description.setSpannableFactory(NoCopySpannableFactory.getInstance());
markwon.setMarkdown(holder.description, release.getDescription());
holder.description.setMovementMethod(null);
- holder.openInBrowser.setOnClickListener(v -> NavUtil.startURL(RepoItemActivity.this, release.getUrl()));
+ holder.openInBrowser.setOnClickListener(v -> NavUtil.startURL(requireActivity(), release.getUrl()));
List assets = release.getReleaseAssets();
if (assets != null && !assets.isEmpty()) {
holder.viewAssets.setOnClickListener(v -> {
ArrayList names = new ArrayList<>();
assets.forEach(releaseAsset -> names.add(releaseAsset.getName()));
- new AlertDialog.Builder(RepoItemActivity.this)
- .setItems(names.toArray(new String[0]), (dialog, which) -> NavUtil.startURL(RepoItemActivity.this, assets.get(which).getDownloadUrl()))
+ new AlertDialog.Builder(requireActivity())
+ .setItems(names.toArray(new String[0]), (dialog, which) -> NavUtil.startURL(requireActivity(), assets.get(which).getDownloadUrl()))
.show();
});
} else {
@@ -350,7 +330,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
}
- class ReleaseViewHolder extends ViewHolder {
+ class ReleaseViewHolder extends ReleaseAdapter.ViewHolder {
public ReleaseViewHolder(ItemRepoReleaseBinding binding) {
super(binding.getRoot());
title = binding.title;
@@ -360,7 +340,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
}
- class LoadmoreViewHolder extends ViewHolder {
+ class LoadmoreViewHolder extends ReleaseAdapter.ViewHolder {
public LoadmoreViewHolder(ItemRepoLoadmoreBinding binding) {
super(binding.getRoot());
title = binding.title;
@@ -375,17 +355,17 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
@Override
public PagerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
- return new ReadmeViewHolder(ItemRepoReadmeBinding.inflate(getLayoutInflater(), parent, false));
+ return new PagerAdapter.ReadmeViewHolder(ItemRepoReadmeBinding.inflate(getLayoutInflater(), parent, false));
} else {
- return new RecyclerviewBinding(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false));
+ return new PagerAdapter.RecyclerviewBinding(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false));
}
}
@Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull PagerAdapter.ViewHolder holder, int position) {
switch (position) {
case 0:
- holder.textView.setTransformationMethod(new LinkTransformationMethod(RepoItemActivity.this));
+ holder.textView.setTransformationMethod(new LinkTransformationMethod(requireActivity()));
holder.scrollView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
holder.scrollView.setTag(position);
markwon.setMarkdown(holder.textView, module.getReadme());
@@ -398,7 +378,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
holder.recyclerView.setAdapter(new InformationAdapter(module));
}
holder.recyclerView.setTag(position);
- holder.recyclerView.setLayoutManager(new LinearLayoutManagerFix(RepoItemActivity.this));
+ holder.recyclerView.setLayoutManager(new LinearLayoutManagerFix(requireActivity()));
holder.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top));
RecyclerViewKt.fixEdgeEffect(holder.recyclerView, false, true);
RecyclerViewKt.addFastScroller(holder.recyclerView, holder.itemView);
@@ -426,7 +406,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
}
- class ReadmeViewHolder extends ViewHolder {
+ class ReadmeViewHolder extends PagerAdapter.ViewHolder {
public ReadmeViewHolder(ItemRepoReadmeBinding binding) {
super(binding.getRoot());
textView = binding.readme;
@@ -434,7 +414,7 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
}
}
- class RecyclerviewBinding extends ViewHolder {
+ class RecyclerviewBinding extends PagerAdapter.ViewHolder {
public RecyclerviewBinding(ItemRepoRecyclerviewBinding binding) {
super(binding.getRoot());
recyclerView = binding.recyclerView;
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java
new file mode 100644
index 00000000..06a92412
--- /dev/null
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java
@@ -0,0 +1,262 @@
+package org.lsposed.manager.ui.fragment;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.snackbar.Snackbar;
+import com.takisoft.preferencex.PreferenceCategory;
+import com.takisoft.preferencex.PreferenceFragmentCompat;
+
+import org.lsposed.manager.App;
+import org.lsposed.manager.BuildConfig;
+import org.lsposed.manager.ConfigManager;
+import org.lsposed.manager.R;
+import org.lsposed.manager.databinding.FragmentSettingsBinding;
+import org.lsposed.manager.ui.activity.MainActivity;
+import org.lsposed.manager.util.BackupUtils;
+import org.lsposed.manager.util.theme.ThemeUtil;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import rikka.core.util.ResourceUtils;
+import rikka.material.app.DayNightDelegate;
+import rikka.recyclerview.RecyclerViewKt;
+import rikka.widget.borderview.BorderRecyclerView;
+
+public class SettingsFragment extends BaseFragment {
+ FragmentSettingsBinding binding;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = FragmentSettingsBinding.inflate(inflater, container, false);
+ binding.getRoot().bringChildToFront(binding.appBar);
+ setupToolbar(binding.toolbar, R.string.Settings);
+ if (savedInstanceState == null) {
+ getChildFragmentManager().beginTransaction()
+ .add(R.id.container, new PreferenceFragment()).commit();
+ }
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ if (ConfigManager.getXposedVersionName() == null) {
+ Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show();
+ }
+ }
+
+
+ public static class PreferenceFragment extends PreferenceFragmentCompat {
+ ActivityResultLauncher backupLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+ uri -> {
+ if (uri != null) {
+ try {
+ // grantUriPermission might throw RemoteException on MIUI
+ requireActivity().grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ AlertDialog alertDialog = new AlertDialog.Builder(requireActivity())
+ .setCancelable(false)
+ .setMessage(R.string.settings_backuping)
+ .show();
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ boolean success = BackupUtils.backup(requireContext(), uri);
+ try {
+ SettingsFragment fragment = (SettingsFragment) getParentFragment();
+ requireActivity().runOnUiThread(() -> {
+ alertDialog.dismiss();
+ fragment.makeSnackBar(success ? R.string.settings_backup_success : R.string.settings_backup_failed, Snackbar.LENGTH_SHORT);
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ });
+ ActivityResultLauncher restoreLauncher = registerForActivityResult(new ActivityResultContracts.OpenDocument(),
+ uri -> {
+ if (uri != null) {
+ try {
+ // grantUriPermission might throw RemoteException on MIUI
+ requireActivity().grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ AlertDialog alertDialog = new AlertDialog.Builder(requireActivity())
+ .setCancelable(false)
+ .setMessage(R.string.settings_restoring)
+ .show();
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ boolean success = BackupUtils.restore(requireContext(), uri);
+ try {
+ SettingsFragment fragment = (SettingsFragment) getParentFragment();
+ requireActivity().runOnUiThread(() -> {
+ alertDialog.dismiss();
+ fragment.makeSnackBar(success ? R.string.settings_restore_success : R.string.settings_restore_failed, Snackbar.LENGTH_SHORT);
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ });
+
+ @Override
+ public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.prefs);
+
+ boolean installed = ConfigManager.getXposedVersionName() != null;
+ SwitchPreference prefVerboseLogs = findPreference("disable_verbose_log");
+ if (prefVerboseLogs != null) {
+ if (requireActivity().getApplicationInfo().uid / 100000 != 0) {
+ prefVerboseLogs.setVisible(false);
+ } else {
+ prefVerboseLogs.setEnabled(installed);
+ prefVerboseLogs.setChecked(!ConfigManager.isVerboseLogEnabled());
+ prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean result = ConfigManager.setVerboseLogEnabled(!(boolean) newValue);
+ SettingsFragment fragment = (SettingsFragment) getParentFragment();
+ if (result && fragment != null) {
+ Snackbar.make(fragment.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT)
+ .setAction(R.string.reboot, v -> ConfigManager.reboot(false, null, false))
+ .show();
+ }
+ return result;
+ });
+ }
+ }
+
+ SwitchPreference prefEnableResources = findPreference("enable_resources");
+ if (prefEnableResources != null) {
+ prefEnableResources.setEnabled(installed);
+ prefEnableResources.setChecked(ConfigManager.isResourceHookEnabled());
+ prefEnableResources.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setResourceHookEnabled((boolean) newValue));
+ }
+
+ Preference backup = findPreference("backup");
+ if (backup != null) {
+ backup.setEnabled(installed);
+ backup.setOnPreferenceClickListener(preference -> {
+ Calendar now = Calendar.getInstance();
+ backupLauncher.launch(String.format(Locale.US,
+ "LSPosed_%04d%02d%02d_%02d%02d%02d.lsp",
+ 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)));
+ return true;
+ });
+ }
+
+ Preference restore = findPreference("restore");
+ if (restore != null) {
+ restore.setEnabled(installed);
+ restore.setOnPreferenceClickListener(preference -> {
+ restoreLauncher.launch(new String[]{"*/*"});
+ return true;
+ });
+ }
+
+ Preference theme = findPreference("dark_theme");
+ if (theme != null) {
+ theme.setOnPreferenceChangeListener((preference, newValue) -> {
+ if (!App.getPreferences().getString("dark_theme", ThemeUtil.MODE_NIGHT_FOLLOW_SYSTEM).equals(newValue)) {
+ DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme((String) newValue));
+ MainActivity activity = (MainActivity) getActivity();
+ if (activity != null) {
+ activity.restart();
+ }
+ }
+ return true;
+ });
+ }
+
+ Preference black_dark_theme = findPreference("black_dark_theme");
+ if (black_dark_theme != null) {
+ black_dark_theme.setOnPreferenceChangeListener((preference, newValue) -> {
+ MainActivity activity = (MainActivity) getActivity();
+ if (activity != null && ResourceUtils.isNightMode(getResources().getConfiguration())) {
+ activity.restart();
+ }
+ return true;
+ });
+ }
+
+ Preference primary_color = findPreference("theme_color");
+ if (primary_color != null) {
+ primary_color.setOnPreferenceChangeListener((preference, newValue) -> {
+ MainActivity activity = (MainActivity) getActivity();
+ if (activity != null) {
+ activity.restart();
+ }
+ return true;
+ });
+ }
+
+ PreferenceCategory prefGroupSystem = findPreference("settings_group_system");
+ SwitchPreference prefShowHiddenIcons = findPreference("show_hidden_icon_apps_enabled");
+ if (prefGroupSystem != null && prefShowHiddenIcons != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ && requireActivity().checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED) {
+ prefGroupSystem.setVisible(true);
+ prefShowHiddenIcons.setVisible(true);
+ prefShowHiddenIcons.setChecked(Settings.Global.getInt(
+ requireActivity().getContentResolver(), "show_hidden_icon_apps_enabled", 1) != 0);
+ prefShowHiddenIcons.setOnPreferenceChangeListener((preference, newValue) -> Settings.Global.putInt(requireActivity().getContentResolver(),
+ "show_hidden_icon_apps_enabled", (boolean) newValue ? 1 : 0));
+ }
+
+ SwitchPreference prefFollowSystemAccent = findPreference("follow_system_accent");
+ if (prefFollowSystemAccent != null && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || Build.VERSION.SDK_INT == Build.VERSION_CODES.R && Build.VERSION.PREVIEW_SDK_INT != 0)) {
+ if (primary_color != null) {
+ primary_color.setVisible(!prefFollowSystemAccent.isChecked());
+ }
+ prefFollowSystemAccent.setVisible(true);
+ prefFollowSystemAccent.setOnPreferenceChangeListener((preference, newValue) -> {
+ MainActivity activity = (MainActivity) getActivity();
+ if (activity != null) {
+ activity.restart();
+ }
+ return true;
+ });
+ }
+ }
+
+ @Override
+ public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ BorderRecyclerView recyclerView = (BorderRecyclerView) super.onCreateRecyclerView(inflater, parent, savedInstanceState);
+ RecyclerViewKt.fixEdgeEffect(recyclerView, false, true);
+ recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> {
+ SettingsFragment fragment = (SettingsFragment) getParentFragment();
+ if (fragment != null) {
+ fragment.binding.appBar.setRaised(!top);
+ }
+ });
+ return recyclerView;
+ }
+ }
+
+ public void makeSnackBar(@StringRes int text, @Snackbar.Duration int duration) {
+ Snackbar.make(binding.snackbar, text, duration).show();
+ }
+}
diff --git a/app/src/main/java/org/lsposed/manager/util/NavUtil.java b/app/src/main/java/org/lsposed/manager/util/NavUtil.java
index 88047171..b465e480 100644
--- a/app/src/main/java/org/lsposed/manager/util/NavUtil.java
+++ b/app/src/main/java/org/lsposed/manager/util/NavUtil.java
@@ -20,6 +20,7 @@
package org.lsposed.manager.util;
+import android.app.Activity;
import android.net.Uri;
import androidx.browser.customtabs.CustomTabColorSchemeParams;
@@ -32,7 +33,7 @@ import rikka.core.util.ResourceUtils;
public final class NavUtil {
- public static void startURL(BaseActivity activity, Uri uri) {
+ public static void startURL(Activity activity, Uri uri) {
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
customTabsIntent.setShowTitle(true);
CustomTabColorSchemeParams params = new CustomTabColorSchemeParams.Builder()
@@ -46,7 +47,7 @@ public final class NavUtil {
customTabsIntent.build().launchUrl(activity, uri);
}
- public static void startURL(BaseActivity activity, String url) {
+ public static void startURL(Activity activity, String url) {
startURL(activity, Uri.parse(url));
}
}
diff --git a/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java b/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java
index 3850fffe..f0b1cb1c 100644
--- a/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java
+++ b/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java
@@ -29,7 +29,7 @@ import android.content.Intent;
import androidx.core.app.NotificationCompat;
import org.lsposed.manager.R;
-import org.lsposed.manager.ui.activity.AppListActivity;
+import org.lsposed.manager.ui.activity.MainActivity;
public final class NotificationUtil {
@@ -51,7 +51,7 @@ public final class NotificationUtil {
String title = context.getString(enabled ? R.string.xposed_module_updated_notification_title : R.string.module_is_not_activated_yet);
String content = context.getString(enabled ? R.string.xposed_module_updated_notification_content : R.string.module_is_not_activated_yet_detailed, moduleName);
- Intent intent = new Intent(context, AppListActivity.class)
+ Intent intent = new Intent(context, MainActivity.class)
.putExtra("modulePackageName", modulePackageName)
.putExtra("moduleUserId", moduleUserId)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/app/src/main/java/org/lsposed/manager/util/chrome/CustomTabsURLSpan.java b/app/src/main/java/org/lsposed/manager/util/chrome/CustomTabsURLSpan.java
index 905d0854..ccc57e9b 100644
--- a/app/src/main/java/org/lsposed/manager/util/chrome/CustomTabsURLSpan.java
+++ b/app/src/main/java/org/lsposed/manager/util/chrome/CustomTabsURLSpan.java
@@ -20,6 +20,7 @@
package org.lsposed.manager.util.chrome;
+import android.app.Activity;
import android.text.style.URLSpan;
import android.view.View;
@@ -28,9 +29,9 @@ import org.lsposed.manager.util.NavUtil;
public class CustomTabsURLSpan extends URLSpan {
- private final BaseActivity activity;
+ private final Activity activity;
- public CustomTabsURLSpan(BaseActivity activity, String url) {
+ public CustomTabsURLSpan(Activity activity, String url) {
super(url);
this.activity = activity;
}
diff --git a/app/src/main/java/org/lsposed/manager/util/chrome/LinkTransformationMethod.java b/app/src/main/java/org/lsposed/manager/util/chrome/LinkTransformationMethod.java
index d4888e27..196f33c1 100644
--- a/app/src/main/java/org/lsposed/manager/util/chrome/LinkTransformationMethod.java
+++ b/app/src/main/java/org/lsposed/manager/util/chrome/LinkTransformationMethod.java
@@ -20,6 +20,7 @@
package org.lsposed.manager.util.chrome;
+import android.app.Activity;
import android.graphics.Rect;
import android.text.Spannable;
import android.text.Spanned;
@@ -32,9 +33,9 @@ import org.lsposed.manager.ui.activity.base.BaseActivity;
public class LinkTransformationMethod implements TransformationMethod {
- private final BaseActivity activity;
+ private final Activity activity;
- public LinkTransformationMethod(BaseActivity activity) {
+ public LinkTransformationMethod(Activity activity) {
this.activity = activity;
}
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
new file mode 100644
index 00000000..e9a2ed45
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/layout-sw600dp/fragment_main.xml b/app/src/main/res/layout-sw600dp/fragment_main.xml
new file mode 100644
index 00000000..d3b6af42
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp/fragment_main.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 01523607..3080eb78 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,321 +1,21 @@
-
-
-
+
+ android:layout_height="match_parent">
-
+ app:defaultNavHost="true"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:navGraph="@navigation/nav_graph" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/app/src/main/res/layout/activity_app_list.xml b/app/src/main/res/layout/fragment_app_list.xml
similarity index 100%
rename from app/src/main/res/layout/activity_app_list.xml
rename to app/src/main/res/layout/fragment_app_list.xml
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 00000000..9f479d7d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,325 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_logs.xml b/app/src/main/res/layout/fragment_logs.xml
similarity index 100%
rename from app/src/main/res/layout/activity_logs.xml
rename to app/src/main/res/layout/fragment_logs.xml
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 00000000..f57b156d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_module_detail.xml b/app/src/main/res/layout/fragment_pager.xml
similarity index 100%
rename from app/src/main/res/layout/activity_module_detail.xml
rename to app/src/main/res/layout/fragment_pager.xml
diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/fragment_repo.xml
similarity index 100%
rename from app/src/main/res/layout/activity_list.xml
rename to app/src/main/res/layout/fragment_repo.xml
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/fragment_settings.xml
similarity index 100%
rename from app/src/main/res/layout/activity_settings.xml
rename to app/src/main/res/layout/fragment_settings.xml
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 00000000..01457f96
--- /dev/null
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/navigation/sub_nav_graph.xml b/app/src/main/res/navigation/sub_nav_graph.xml
new file mode 100644
index 00000000..f5c3cfe3
--- /dev/null
+++ b/app/src/main/res/navigation/sub_nav_graph.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 9aa7f8fa..cd4de515 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -22,4 +22,6 @@
48dp
48dp
32dp
-
\ No newline at end of file
+ 400dp
+ 8dp
+
diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml
index e2017971..5d974e29 100644
--- a/app/src/main/res/xml/shortcuts.xml
+++ b/app/src/main/res/xml/shortcuts.xml
@@ -28,29 +28,32 @@
android:shortcutShortLabel="@string/Modules">