From 61de7ca6e3b2f163d68c54facf178c28059dc0e6 Mon Sep 17 00:00:00 2001
From: Fan <732388462@qq.com>
Date: Sun, 19 May 2024 16:25:21 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?=
=?UTF-8?q?=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 13 +-
.../ui/HyperCeilerTabActivity.java | 152 ++++++++++++
.../ui/base/HyperCeilerTabBaseActivity.java | 108 ++++++++
.../ui/base/NavigationActivity.java | 14 --
.../hyperceiler/ui/page/ContentFragment.java | 231 ++++++++++++++++++
.../ui/page/DraggableViewPager.java | 49 ++++
.../ui/page/FragmentPagerAdapter.java | 159 ++++++++++++
.../hyperceiler/ui/page/TabViewModel.java | 27 ++
app/src/main/res/drawable/ic_reboot_small.xml | 12 +-
app/src/main/res/layout/fragment_content.xml | 27 ++
app/src/main/res/menu/bottom_nav_menu.xml | 25 ++
app/src/main/res/values/themes.xml | 31 +++
12 files changed, 830 insertions(+), 18 deletions(-)
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/HyperCeilerTabActivity.java
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/base/HyperCeilerTabBaseActivity.java
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/page/ContentFragment.java
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/page/DraggableViewPager.java
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/page/FragmentPagerAdapter.java
create mode 100644 app/src/main/java/com/sevtinge/hyperceiler/ui/page/TabViewModel.java
create mode 100644 app/src/main/res/layout/fragment_content.xml
create mode 100644 app/src/main/res/menu/bottom_nav_menu.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c8d469377b..0d3700c672 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -67,6 +67,17 @@
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/HyperCeilerTabActivity.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/HyperCeilerTabActivity.java
new file mode 100644
index 0000000000..2069b3d800
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/HyperCeilerTabActivity.java
@@ -0,0 +1,152 @@
+package com.sevtinge.hyperceiler.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+
+import androidx.annotation.Nullable;
+
+import com.sevtinge.hyperceiler.R;
+import com.sevtinge.hyperceiler.callback.IResult;
+import com.sevtinge.hyperceiler.prefs.PreferenceHeader;
+import com.sevtinge.hyperceiler.safe.CrashData;
+import com.sevtinge.hyperceiler.ui.base.HyperCeilerTabBaseActivity;
+import com.sevtinge.hyperceiler.utils.BackupUtils;
+import com.sevtinge.hyperceiler.utils.Helpers;
+import com.sevtinge.hyperceiler.utils.LanguageHelper;
+import com.sevtinge.hyperceiler.utils.PropUtils;
+import com.sevtinge.hyperceiler.utils.ThreadPoolManager;
+import com.sevtinge.hyperceiler.utils.api.ProjectApi;
+import com.sevtinge.hyperceiler.utils.prefs.PrefsUtils;
+import com.sevtinge.hyperceiler.utils.search.SearchHelper;
+import com.sevtinge.hyperceiler.utils.shell.ShellInit;
+
+import java.util.ArrayList;
+
+import fan.appcompat.app.AlertDialog;
+
+public class HyperCeilerTabActivity extends HyperCeilerTabBaseActivity implements IResult {
+
+ private Handler handler;
+ private Context context;
+
+ private ArrayList appCrash = new ArrayList<>();
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ SharedPreferences mPrefs = PrefsUtils.mSharedPreferences;
+ int count = Integer.parseInt(mPrefs.getString("prefs_key_settings_app_language", "-1"));
+ if (count != -1) {
+ LanguageHelper.setIndexLanguage(this, count, false);
+ }
+ handler = new Handler(this.getMainLooper());
+ context = this;
+ int def = Integer.parseInt(PrefsUtils.mSharedPreferences.getString("prefs_key_log_level", "3"));
+ super.onCreate(bundle);
+ new Thread(() -> SearchHelper.getAllMods(this, bundle != null)).start();
+ Helpers.checkXposedActivateState(this);
+ ShellInit.init(this);
+ PropUtils.setProp("persist.hyperceiler.log.level", ProjectApi.isCanary() ? (def != 3 && def != 4 ? 3 : def) : def);
+ appCrash = CrashData.toPkgList();
+ handler.postDelayed(() -> {
+ if (haveCrashReport()) {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.safe_mode_later_title)
+ .setMessage(appCrash.toString() + " " + getString(R.string.safe_mode_later_desc))
+ .setHapticFeedbackEnabled(true)
+ .setCancelable(false)
+ .setPositiveButton(R.string.safe_mode_cancel, (dialog, which) -> {
+ ShellInit.getShell().run("setprop persist.hyperceiler.crash.report \"\"").sync();
+ dialog.dismiss();
+ })
+ .setNegativeButton(R.string.safe_mode_ok, (dialog, which) -> dialog.dismiss())
+ .show();
+ }
+ }, 600);
+ }
+
+ @Override
+ public void error(String reason) {
+ handler.post(() -> new AlertDialog.Builder(context)
+ .setCancelable(false)
+ .setTitle(getResources().getString(R.string.tip))
+ .setMessage(getResources().getString(R.string.root))
+ .setHapticFeedbackEnabled(true)
+ .setPositiveButton(android.R.string.ok, null)
+ .show());
+ }
+
+ private boolean haveCrashReport() {
+ return !appCrash.isEmpty();
+ }
+
+ @Override
+ public void onDestroy() {
+ ShellInit.destroy();
+ ThreadPoolManager.shutdown();
+ PreferenceHeader.mUninstallApp.clear();
+ PreferenceHeader.mDisableOrHiddenApp.clear();
+ super.onDestroy();
+ }
+
+ public void test() {
+ /*boolean ls = shellExec.append("ls").sync().isResult();
+ AndroidLogUtils.LogI(ITAG.TAG, "ls: " + ls);
+ AndroidLogUtils.LogI(ITAG.TAG, shellExec.getOutPut().toString() + shellExec.getError().toString());
+ boolean f = shellExec.append("for i in $(seq 1 500); do echo $i; done").isResult();
+ AndroidLogUtils.LogI(ITAG.TAG, "for: " + f);
+ AndroidLogUtils.LogI(ITAG.TAG, shellExec.getOutPut().toString());
+ boolean k = shellExec.append("for i in $(seq 1 500); do echo $i; done").sync().isResult();
+ AndroidLogUtils.LogI(ITAG.TAG, "fork: " + k);
+ AndroidLogUtils.LogI(ITAG.TAG, shellExec.getOutPut().toString());*/
+ }
+
+ private void requestCta() {
+ /*if (!CtaUtils.isCtaEnabled(this)) {
+ CtaUtils.showCtaDialog(this, REQUEST_CODE);
+ }*/
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ requestCta();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (data == null) return;
+ try {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ switch (requestCode) {
+ case BackupUtils.CREATE_DOCUMENT_CODE -> {
+ BackupUtils.handleCreateDocument(this, data.getData());
+ alert.setTitle(R.string.backup_success);
+ }
+ case BackupUtils.OPEN_DOCUMENT_CODE -> {
+ BackupUtils.handleReadDocument(this, data.getData());
+ alert.setTitle(R.string.rest_success);
+ }
+ default -> {
+ return;
+ }
+ }
+ alert.setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ });
+ alert.show();
+ } catch (Exception e) {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ switch (requestCode) {
+ case BackupUtils.CREATE_DOCUMENT_CODE -> alert.setTitle(R.string.backup_failed);
+ case BackupUtils.OPEN_DOCUMENT_CODE -> alert.setTitle(R.string.rest_failed);
+ }
+ alert.setMessage(e.toString());
+ alert.setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ });
+ alert.show();
+ }
+ }
+}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/base/HyperCeilerTabBaseActivity.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/base/HyperCeilerTabBaseActivity.java
new file mode 100644
index 0000000000..9b0ca1bb48
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/base/HyperCeilerTabBaseActivity.java
@@ -0,0 +1,108 @@
+package com.sevtinge.hyperceiler.ui.base;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+import com.sevtinge.hyperceiler.R;
+import com.sevtinge.hyperceiler.ui.SubSettings;
+import com.sevtinge.hyperceiler.ui.page.ContentFragment;
+import com.sevtinge.hyperceiler.utils.SettingLauncherHelper;
+
+import fan.appcompat.app.Fragment;
+import fan.navigator.Navigator;
+import fan.navigator.NavigatorStrategy;
+import fan.navigator.app.NavigatorActivity;
+import fan.navigator.navigatorinfo.NavigatorInfoProvider;
+import fan.navigator.navigatorinfo.UpdateFragmentNavInfo;
+import fan.preference.Preference;
+import fan.preference.PreferenceFragment;
+import fan.preference.core.PreferenceFragmentCompat;
+
+public class HyperCeilerTabBaseActivity extends NavigatorActivity
+ implements PreferenceFragment.OnPreferenceStartFragmentCallback {
+
+ @Override
+ public int getBottomTabMenu() {
+ return R.menu.bottom_nav_menu;
+ }
+
+ @Override
+ public NavigatorInfoProvider getBottomTabMenuNavInfoProvider() {
+ Bundle bundle = new Bundle();
+ return i -> {
+ if (i == 1000) {
+ bundle.putInt("page", 0);
+ } else if (i == 1001) {
+ bundle.putInt("page", 1);
+ } else if (i == 1002) {
+ bundle.putInt("page", 2);
+ } else {
+ return null;
+ }
+ return new UpdateFragmentNavInfo(i, getDefaultContentFragment(), bundle);
+ };
+ }
+
+ @Override
+ public Class extends Fragment> getDefaultContentFragment() {
+ return ContentFragment.class;
+ }
+
+ @Override
+ public int getNavigationOptionMenu() {
+ return 0;
+ }
+
+ @Override
+ public Bundle getNavigatorInitArgs() {
+ NavigatorStrategy navigatorStrategy = new NavigatorStrategy();
+ navigatorStrategy.setLargeMode(Navigator.Mode.C);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("miuix:navigatorStrategy", navigatorStrategy);
+ return bundle;
+ }
+
+ @Override
+ public void onCreateOtherNavigation(Navigator navigator, Bundle bundle) {
+
+ }
+
+ @Override
+ public void onCreatePrimaryNavigation(Navigator navigator, Bundle bundle) {
+ UpdateFragmentNavInfo updateFragmentNavInfoToHome = getUpdateFragmentNavInfoToHome();
+ UpdateFragmentNavInfo updateFragmentNavInfoToSettings = updateFragmentNavInfoToSettings();
+ UpdateFragmentNavInfo updateFragmentNavInfoToClock = getUpdateFragmentNavInfoToAbout();
+ newLabel(getString(R.string.home), updateFragmentNavInfoToHome);
+ newLabel(getString(R.string.settings), updateFragmentNavInfoToSettings);
+ newLabel(getString(R.string.about), updateFragmentNavInfoToClock);
+ navigator.navigate(updateFragmentNavInfoToHome);
+ }
+
+ private UpdateFragmentNavInfo getUpdateFragmentNavInfoToHome() {
+ Bundle bundle = new Bundle();
+ bundle.putInt("page", 0);
+ return new UpdateFragmentNavInfo(1000, getDefaultContentFragment(), bundle);
+ }
+
+ private UpdateFragmentNavInfo updateFragmentNavInfoToSettings() {
+ Bundle bundle = new Bundle();
+ bundle.putInt("page", 1);
+ return new UpdateFragmentNavInfo(1001, getDefaultContentFragment(), bundle);
+ }
+
+ private UpdateFragmentNavInfo getUpdateFragmentNavInfoToAbout() {
+ Bundle bundle = new Bundle();
+ bundle.putInt("page", 2);
+ return new UpdateFragmentNavInfo(1002, getDefaultContentFragment(), bundle);
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat preferenceFragmentCompat, @NonNull Preference preference) {
+ Bundle args = null;
+ String mFragmentName = preference.getFragment();
+ String mTitle = preference.getTitle().toString();
+ SettingLauncherHelper.onStartSettingsForArguments(this, SubSettings.class, mFragmentName, args, mTitle);
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/base/NavigationActivity.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/base/NavigationActivity.java
index 05b74683c0..039b807124 100644
--- a/app/src/main/java/com/sevtinge/hyperceiler/ui/base/NavigationActivity.java
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/base/NavigationActivity.java
@@ -259,18 +259,4 @@ public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat prefe
mProxy.onStartSettingsForArguments(SubSettings.class, preference, false);
return true;
}
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- if (item.getItemId() == R.id.restart) {
- DialogHelper.showRestartDialog(this);
- }
- return super.onOptionsItemSelected(item);
- }
}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/page/ContentFragment.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/ContentFragment.java
new file mode 100644
index 0000000000..bbd18ff43f
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/ContentFragment.java
@@ -0,0 +1,231 @@
+package com.sevtinge.hyperceiler.ui.page;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+
+import com.sevtinge.hyperceiler.R;
+import com.sevtinge.hyperceiler.utils.DialogHelper;
+
+import fan.appcompat.app.ActionBar;
+import fan.appcompat.app.Fragment;
+import fan.navigator.Navigator;
+import fan.navigator.NavigatorFragmentListener;
+import fan.navigator.navigatorinfo.UpdateFragmentNavInfo;
+import fan.nestedheader.widget.NestedHeaderLayout;
+
+public class ContentFragment extends Fragment implements NavigatorFragmentListener {
+
+ private static final String TAG = "ContentFragment";
+ public static final String ARG_PAGE = "page";
+ public static String mCurrTab = "HOME";
+ private ActionBar mActionBar;
+ private NestedHeaderLayout mNestedHeaderLayout;
+ private DraggableViewPager mViewPager;
+ private FragmentPagerAdapter mViewPagerAdapter;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setThemeRes(R.style.TabNavigatorContentFragmentTheme);
+ }
+
+ @Override
+ public View onInflateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_content, container, false);
+ }
+
+ @Override
+ public void onUpdateArguments(Bundle args) {
+ super.onUpdateArguments(args);
+ if (mViewPager != null) {
+ int position = args.getInt(ARG_PAGE, mViewPager.getCurrentItem());
+ mViewPager.setCurrentItem(position, mViewPager.isDraggable());
+ }
+ }
+
+ @Override
+ public void onNavigatorModeChanged(Navigator.Mode mode, Navigator.Mode mode2) {
+ if (getView() != null) {
+ Navigator.get(this);
+ mViewPager.setDraggable(true);
+ invalidateOptionsMenu();
+ }
+ }
+
+ @Override
+ public void onViewInflated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewInflated(view, savedInstanceState);
+ setCorrectNestedScrollMotionEventEnabled(true);
+ mNestedHeaderLayout = view.findViewById(R.id.nested_header);
+ mViewPager = view.findViewById(R.id.viewpager);
+ registerCoordinateScrollView(mNestedHeaderLayout);
+ Navigator.get(this).setTabSelectListener((menuItem, info) -> {
+ int position = 0;
+ switch (info.getNavigationId()) {
+ case 1000 -> position = 0;
+ case 1001 -> position = 1;
+ case 1002 -> position = 2;
+ }
+ mViewPager.setCurrentItem(position, mViewPager.isDraggable());
+ return true;
+ });
+ setupViewPager();
+ }
+
+ private void setupViewPager() {
+ mActionBar = getActionBar();
+ mActionBar.setResizable(true);
+ mActionBar.setDisplayShowCustomEnabled(false);
+ mActionBar.setDisplayShowTitleEnabled(true);
+ mActionBar.setDisplayHomeAsUpEnabled(false);
+ Navigator navigator = Navigator.get(this);
+ mViewPager.setDraggable(false);
+ mViewPagerAdapter = new FragmentPagerAdapter(getAppCompatActivity(), getChildFragmentManager(), mCurrTab);
+ mViewPager.setOffscreenPageLimit(4);
+ mViewPager.setAdapter(mViewPagerAdapter);
+ if (getArguments() != null && getArguments().containsKey(ARG_PAGE)) {
+ int item = getArguments().getInt(ARG_PAGE);
+ mViewPager.setCurrentItem(item);
+ if (navigator != null && navigator.getBottomTabMenu().size() > 0) {
+ navigator.getBottomTabMenu().getItem(item).setChecked(true);
+ }
+ setupHyperOSViewPager(item);
+ }
+
+ mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ String leftTab;
+ float offSet = 1.0f;
+ boolean isHandUp = false;
+ boolean isHandScroll = false;
+ boolean isPageChanged;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ if (isHandScroll || (isHandUp && !isPageChanged)) {
+ String tabAt = TabViewModel.getTabAt(position);
+ leftTab = tabAt;
+ if (tabAt.equals(mCurrTab)) {
+ offSet = 1.0f - positionOffset;
+ } else if (position == TabViewModel.getTabPosition(mCurrTab) - 1) {
+ offSet = positionOffset;
+ } else {
+ offSet = 0.5f;
+ }
+ if (offSet < 0.5f) {
+ offSet = 0.5f;
+ }
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ String tabAt = TabViewModel.getTabAt(position);
+ navigator.selectTab(position);
+ setupHyperOSViewPager(position);
+ handleFragmentChange(mCurrTab, tabAt);
+ offSet = 1.0f;
+ mCurrTab = tabAt;
+ selectNavigationItem(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (state == 1) {
+ isPageChanged = false;
+ isHandScroll = true;
+ isHandUp = false;
+ } else if (state == 2) {
+ isHandScroll = false;
+ isHandUp = true;
+ } else {
+ isHandScroll = false;
+ isHandUp = false;
+ }
+ }
+ });
+ }
+
+ private void setupHyperOSViewPager(int position) {
+ if (mActionBar != null) {
+ if (position == 0) {
+ mActionBar.setTitle(R.string.home);
+ } else if (position == 1) {
+ mActionBar.setTitle(R.string.settings);
+ } else {
+ mActionBar.setTitle(R.string.about);
+ }
+ }
+ }
+
+ public void selectNavigationItem(int position) {
+ switch (mCurrTab) {
+ case "HOME" -> navigateToHome();
+ case "SETTINGS" -> navigateToSettings();
+ case "ABOUT" -> navigateToAbout();
+ default -> {}
+ }
+ }
+
+ public void handleFragmentChange(String oldTab, String newTab) {
+ Log.d(TAG, "handleFragmentChange: oldTab: " + oldTab + "newTab: " + newTab);
+ if (newTab != null && oldTab != null && !newTab.equals(oldTab)) {
+ androidx.fragment.app.Fragment oldFragment = mViewPagerAdapter.getFragment(oldTab, false);
+ Log.d(TAG, "oldFragment: " + oldFragment);
+ androidx.fragment.app.Fragment newFragment = mViewPagerAdapter.getFragment(newTab, false);
+ Log.d(TAG, "newFragment: " + newFragment);
+ }
+ }
+
+ public void navigateToHome() {
+ Navigator.get(this).navigate(getUpdateFragmentNavInfoToHome());
+ }
+
+ public void navigateToSettings() {
+ Navigator.get(this).navigate(getUpdateFragmentNavInfoToSettings());
+ }
+
+ public void navigateToAbout() {
+ Navigator.get(this).navigate(getUpdateFragmentNavInfoToAbout());
+ }
+
+ private UpdateFragmentNavInfo getUpdateFragmentNavInfoToHome() {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, 0);
+ return new UpdateFragmentNavInfo(1000, getClass(), args);
+ }
+
+ private UpdateFragmentNavInfo getUpdateFragmentNavInfoToSettings() {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, 1);
+ return new UpdateFragmentNavInfo(1001, getClass(), args);
+ }
+
+ private UpdateFragmentNavInfo getUpdateFragmentNavInfoToAbout() {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, 2);
+ return new UpdateFragmentNavInfo(1002, getClass(), args);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == R.id.restart) {
+ DialogHelper.showRestartDialog(requireContext());
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/page/DraggableViewPager.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/DraggableViewPager.java
new file mode 100644
index 0000000000..35c016bea1
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/DraggableViewPager.java
@@ -0,0 +1,49 @@
+package com.sevtinge.hyperceiler.ui.page;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class DraggableViewPager extends androidx.viewpager.widget.ViewPager {
+
+ private boolean mCanDrag = false;
+
+ public DraggableViewPager(@NonNull Context context) {
+ super(context);
+ }
+
+ public DraggableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public boolean isDraggable() {
+ return mCanDrag;
+ }
+
+ public void setDraggable(boolean isDrag) {
+ mCanDrag = isDrag;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ try {
+ return mCanDrag ? super.onInterceptTouchEvent(ev) : false;
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ try {
+ return mCanDrag ? super.onInterceptTouchEvent(ev) : false;
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/page/FragmentPagerAdapter.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/FragmentPagerAdapter.java
new file mode 100644
index 0000000000..5ba04682ee
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/FragmentPagerAdapter.java
@@ -0,0 +1,159 @@
+package com.sevtinge.hyperceiler.ui.page;
+
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.viewpager.widget.PagerAdapter;
+
+import com.sevtinge.hyperceiler.ui.fragment.MainFragment;
+import com.sevtinge.hyperceiler.ui.fragment.base.AboutFragment;
+import com.sevtinge.hyperceiler.ui.fragment.base.settings.ModuleSettingsFragment;
+
+import java.util.Map;
+
+import fan.appcompat.app.AppCompatActivity;
+
+public class FragmentPagerAdapter extends PagerAdapter {
+
+ private static final String TAG = "DynamicFragmentPagerAdapter";
+
+ private String mCurrTab;
+ private int mFragmentSize;
+ private AppCompatActivity mActivity;
+ private FragmentManager mFragmentManager;
+ private Fragment mCurrentPrimaryItem = null;
+ private FragmentTransaction mCurTransaction = null;
+ private final Map mFragmentCache;
+
+
+ public FragmentPagerAdapter(AppCompatActivity activity, FragmentManager fragmentManager, String tag) {
+ Log.d(TAG, "init, currTab: " + tag);
+ mCurrTab = tag;
+ mActivity = activity;
+ mFragmentManager = fragmentManager;
+ mFragmentCache = new ArrayMap(getCount());
+ addFragment("HOME", MainFragment.class);
+ addFragment("SETTINGS", ModuleSettingsFragment.class);
+ addFragment("ABOUT", AboutFragment.class);
+ mFragmentSize = 3;
+ }
+
+ private String getTabAt(int position) {
+ return TabViewModel.getTabAt(position);
+ }
+
+ public void addFragment(String tag, Class extends Fragment> clazz) {
+ mFragmentCache.put(tag, new FragmentInfo(tag, clazz, false));
+ }
+
+ public Fragment getFragment(String tag, boolean z) {
+ FragmentInfo fragmentInfo = mFragmentCache.get(tag);
+ if (fragmentInfo.fragment == null) {
+ fragmentInfo.fragment = mFragmentManager.findFragmentByTag(fragmentInfo.tag);
+ }
+ if (z && fragmentInfo.fragment == null) {
+ fragmentInfo.fragment = Fragment.instantiate(mActivity, fragmentInfo.clazz.getName());
+ }
+ return fragmentInfo.fragment;
+ }
+
+ private Fragment getNewFragment(String tag) {
+ return Fragment.instantiate(mActivity, mFragmentCache.get(tag).clazz.getName());
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ String tabAt = getTabAt(position);
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ Fragment fragment = getFragment(tabAt, true);
+ if (fragment.getFragmentManager() != null) {
+ mCurTransaction.attach(fragment);
+ } else {
+ mCurTransaction.add(container.getId(), fragment, tabAt);
+ }
+ if (fragment != mCurrentPrimaryItem) {
+ fragment.setMenuVisibility(false);
+ fragment.setUserVisibleHint(false);
+ }
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ mCurTransaction.detach((Fragment) object);
+ }
+
+ @Override
+ public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ Fragment fragment = (Fragment) object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ if (fragment != null) {
+ fragment.setMenuVisibility(true);
+ fragment.setUserVisibleHint(true);
+ }
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ @Override
+ public void finishUpdate(@NonNull ViewGroup container) {
+ if (mActivity != null && !mActivity.isDestroyed() && mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ if (!mFragmentManager.isDestroyed()) {
+ mFragmentManager.executePendingTransactions();
+ }
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mFragmentSize;
+ }
+
+ @Override
+ public int getItemPosition(@NonNull Object object) {
+ for (int i = 0; i < getCount(); i++) {
+ if (object == mFragmentCache.get(getTabAt(i)).fragment) {
+ return i;
+ }
+ }
+ return -2;
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+ return ((Fragment) object).getView() == view;
+ }
+
+ public class FragmentInfo {
+
+ String tag;
+ boolean lazyInit;
+ Fragment fragment = null;
+ Class extends Fragment> clazz;
+
+ FragmentInfo(String tag, Class extends Fragment> clazz, boolean lazyInit) {
+ this.tag = tag;
+ this.clazz = clazz;
+ this.lazyInit = lazyInit;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/sevtinge/hyperceiler/ui/page/TabViewModel.java b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/TabViewModel.java
new file mode 100644
index 0000000000..06cb828586
--- /dev/null
+++ b/app/src/main/java/com/sevtinge/hyperceiler/ui/page/TabViewModel.java
@@ -0,0 +1,27 @@
+package com.sevtinge.hyperceiler.ui.page;
+
+public class TabViewModel {
+ public static final String TAB_HOME = "HOME";
+ public static final String TAB_SETTINGS = "SETTINGS";
+ public static final String TAB_ABOUT = "ABOUT";
+ public static final String[] TABS = {TAB_HOME, TAB_SETTINGS, TAB_ABOUT};
+
+ public static String getTabAt(int position) {
+ return TABS[position];
+ }
+
+ public static String getTab(int position) {
+ if (position < 0) return null;
+ if (position >= TABS.length) return null;
+ return TABS[position];
+ }
+
+ public static int getTabPosition(String tab) {
+ for (int i = 0; i < TABS.length; i++) {
+ if (getTabAt(i).equals(tab)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+}
diff --git a/app/src/main/res/drawable/ic_reboot_small.xml b/app/src/main/res/drawable/ic_reboot_small.xml
index 42648737fc..e1a12c9fff 100644
--- a/app/src/main/res/drawable/ic_reboot_small.xml
+++ b/app/src/main/res/drawable/ic_reboot_small.xml
@@ -1,9 +1,15 @@
-
+
+ android:viewportHeight="1024"
+ app:actionIconDisabledAlpha="0.3"
+ app:actionIconNormalAlpha="0.8"
+ app:actionIconPressedAlpha="0.5">
-
+
diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml
new file mode 100644
index 0000000000..137ae305be
--- /dev/null
+++ b/app/src/main/res/layout/fragment_content.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000000..66b199d96b
--- /dev/null
+++ b/app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index c38fd22980..6090651332 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -49,4 +49,35 @@
- @layout/miuix_preference_widget_seekbar
+
+
+
+
+
+
\ No newline at end of file