diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 73d25ad..7e7c8ac 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -3,21 +3,17 @@ package com.fox2code.mmm; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; import androidx.cardview.widget.CardView; -import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.PixelFormat; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.util.TypedValue; import android.view.View; -import android.view.ViewGroup; -import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.TextView; @@ -36,7 +32,8 @@ import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.RenderScriptBlur; public class MainActivity extends CompatActivity implements SwipeRefreshLayout.OnRefreshListener, - SearchView.OnQueryTextListener, SearchView.OnCloseListener { + SearchView.OnQueryTextListener, SearchView.OnCloseListener, + OverScrollManager.OverScrollHelper { private static final String TAG = "MainActivity"; private static final int PRECISION = 10000; public final ModuleViewListBuilder moduleViewListBuilder; @@ -46,6 +43,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O private int swipeRefreshLayoutOrigStartOffset; private int swipeRefreshLayoutOrigEndOffset; private long swipeRefreshBlocker = 0; + private int overScrollInsetTop; + private int overScrollInsetBottom; private TextView actionBarPadding; private BlurView actionBarBlur; private RecyclerView moduleList; @@ -70,9 +69,10 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O this.setTitle(R.string.app_name); this.getWindow().setFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | - WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | - WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + setActionBarBackground(null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { WindowManager.LayoutParams layoutParams = this.getWindow().getAttributes(); layoutParams.layoutInDisplayCutoutMode = // Support cutout in Android 9 @@ -95,6 +95,7 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O this.moduleList.setAdapter(this.moduleViewAdapter); this.moduleList.setLayoutManager(new LinearLayoutManager(this)); this.moduleList.setItemViewCacheSize(4); // Default is 2 + OverScrollManager.install(this.moduleList, this); this.swipeRefreshLayout.setOnRefreshListener(this); this.actionBarBlur.setupWith(this.moduleList).setFrameClearDrawable( this.getWindow().getDecorView().getBackground()) @@ -109,7 +110,7 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O } }); this.searchView.setImeOptions(EditorInfo.IME_ACTION_SEARCH | - EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_FORCE_ASCII); + EditorInfo.IME_FLAG_NO_FULLSCREEN); this.searchView.setOnQueryTextListener(this); this.searchView.setOnCloseListener(this); this.searchView.setOnQueryTextFocusChangeListener((v, h) -> { @@ -230,10 +231,13 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O swipeRefreshLayoutOrigStartOffset + combinedBarsHeight, swipeRefreshLayoutOrigEndOffset + combinedBarsHeight); this.moduleViewListBuilder.setHeaderPx(actionBarHeight); - this.moduleViewListBuilder.setFooterPx((landscape ? 0 : - this.getNavigationBarHeight()) + this.searchCard.getHeight()); + int bottomInset = (landscape ? 0 : this.getNavigationBarHeight()); + this.moduleViewListBuilder.setFooterPx( + bottomInset + this.searchCard.getHeight()); this.moduleViewListBuilder.updateInsets(); this.actionBarBlur.invalidate(); + this.overScrollInsetTop = combinedBarsHeight; + this.overScrollInsetBottom = bottomInset; } @Override @@ -360,4 +364,14 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O } return false; } + + @Override + public int getOverScrollInsetTop() { + return this.overScrollInsetTop; + } + + @Override + public int getOverScrollInsetBottom() { + return this.overScrollInsetBottom; + } } \ No newline at end of file diff --git a/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java b/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java index fc4a173..ec074a2 100644 --- a/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java +++ b/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java @@ -103,6 +103,7 @@ public class ModuleViewListBuilder { this.updating = true; final ArrayList moduleHolders; final int newNotificationsLen; + final boolean first; final ModuleHolder[] headerFooter = new ModuleHolder[2]; try { synchronized (this.updateLock) { @@ -120,6 +121,7 @@ public class ModuleViewListBuilder { moduleHolders.add(new ModuleHolder(notificationType)); } } + first = moduleViewAdapter.moduleHolders.isEmpty(); newNotificationsLen = this.notifications.size() + 1 - special; EnumSet headerTypes = EnumSet.of(ModuleHolder.Type.SEPARATOR, ModuleHolder.Type.NOTIFICATION, ModuleHolder.Type.FOOTER); @@ -170,9 +172,7 @@ public class ModuleViewListBuilder { this.updateInsets = RUNNABLE; final EnumSet oldNotifications = EnumSet.noneOf(NotificationType.class); - boolean isTop = // Force isTop if empty - moduleViewAdapter.moduleHolders.isEmpty() || - !moduleList.canScrollVertically(-1); + boolean isTop = first || !moduleList.canScrollVertically(-1); boolean isBottom = !isTop && !moduleList.canScrollVertically(1); int oldNotificationsLen = 0; int oldOfflineModulesLen = 0; @@ -182,7 +182,8 @@ public class ModuleViewListBuilder { oldNotifications.add(notificationType); if (!notificationType.special) oldNotificationsLen++; - } else if (moduleHolder.footerPx != -1) + } else if (moduleHolder.footerPx != -1 && + moduleHolder.filterLevel == 1) oldNotificationsLen++; // Fix header if (moduleHolder.separator == ModuleHolder.Type.INSTALLABLE) break; diff --git a/app/src/main/java/com/fox2code/mmm/OverScrollManager.java b/app/src/main/java/com/fox2code/mmm/OverScrollManager.java new file mode 100644 index 0000000..0b81b6c --- /dev/null +++ b/app/src/main/java/com/fox2code/mmm/OverScrollManager.java @@ -0,0 +1,92 @@ +package com.fox2code.mmm; + +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.fox2code.mmm.compat.CompatDisplay; +import com.mikepenz.aboutlibraries.LibsConfiguration; + +public class OverScrollManager { + private static final String TAG = "OverScrollManager"; + + public interface OverScrollHelper { + int getOverScrollInsetTop(); + + int getOverScrollInsetBottom(); + } + + public static class LibsOverScroll implements LibsConfiguration.LibsUIListener { + private final OverScrollHelper overScrollHelper; + + public LibsOverScroll() { + this.overScrollHelper = null; + } + + public LibsOverScroll(OverScrollHelper overScrollHelper) { + this.overScrollHelper = overScrollHelper; + } + + @NonNull + @Override + public View preOnCreateView(@NonNull View view) { + return view; + } + + @NonNull + @Override + public View postOnCreateView(@NonNull View view) { + OverScrollManager.install( + view.findViewById(R.id.cardListView), + this.overScrollHelper); + return view; + } + } + + public static void install(final RecyclerView recyclerView) { + OverScrollManager.install(recyclerView, null); + } + + public static void install(final RecyclerView recyclerView, + final OverScrollHelper overScrollHelper) { + if (recyclerView == null) return; + recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + int prevState = -1, lastTranslation = 0; + + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_IDLE && this.prevState != newState) { + if (recyclerView.getOverScrollMode() != View.OVER_SCROLL_NEVER) + recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); + final int threshold = CompatDisplay.dpToPixel(16); + final int lastTranslation = this.lastTranslation; + if (lastTranslation < threshold) { + this.prevState = newState; + return; + } + final int insetTop; + final int insetBottom; + if (overScrollHelper == null) { + insetTop = 0; + insetBottom = 0; + } else { + insetTop = overScrollHelper.getOverScrollInsetTop(); + insetBottom = overScrollHelper.getOverScrollInsetBottom(); + } + Log.d(TAG, "Overscroll: " + lastTranslation + " -> (" + + insetTop + ", " + insetBottom + ")"); + // TODO Overscroll effect for 5.0 (With settings toggle) + } + this.prevState = newState; + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + this.lastTranslation = dy; + } + }); + } +} diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java index 26830f8..c965735 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java @@ -78,11 +78,16 @@ public class AndroidacyActivity extends CompatActivity { String title = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_TITLE); String config = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG); this.setContentView(R.layout.webview); + setActionBarBackground(null); + this.setDisplayHomeAsUpEnabled(true); + if (title == null || title.isEmpty()) { + this.setTitle(title); + } else { + this.setTitle("Androidacy"); + } if (allowInstall || title == null || title.isEmpty()) { this.hideActionBar(); } else { // Only used for note section - this.setTitle(title); - this.setDisplayHomeAsUpEnabled(true); if (config != null && !config.isEmpty()) { String configPkg = IntentHelper.getPackageOfConfig(config); try { diff --git a/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java b/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java index 4783397..87d4675 100644 --- a/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java +++ b/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java @@ -5,6 +5,7 @@ import android.app.Application; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; @@ -17,6 +18,7 @@ import android.view.View; import androidx.annotation.CallSuper; import androidx.annotation.Dimension; import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.annotation.StringRes; @@ -322,6 +324,13 @@ public class CompatActivity extends AppCompatActivity { } } + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + this.compatConfigHelper.checkResourcesOverrides(this.getTheme(), + this.forceEnglish, this.nightModeOverride); + super.onConfigurationChanged(newConfig); + } + public void setOnBackPressedCallback(OnBackPressedCallback onBackPressedCallback) { this.onBackPressedCallback = onBackPressedCallback; } diff --git a/app/src/main/java/com/fox2code/mmm/compat/CompatConfigHelper.java b/app/src/main/java/com/fox2code/mmm/compat/CompatConfigHelper.java index 86ab063..e9655dc 100644 --- a/app/src/main/java/com/fox2code/mmm/compat/CompatConfigHelper.java +++ b/app/src/main/java/com/fox2code/mmm/compat/CompatConfigHelper.java @@ -14,9 +14,13 @@ import java.util.Locale; final class CompatConfigHelper { // ENGLISH like this is an unnatural local, as it doesn't precise the country // All english locales settable by the user precise the country (Ex: en-US) - private static final Locale english = Locale.ENGLISH; + private static final Locale englishLocale = Locale.ENGLISH; + private static final Object englishLocales = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? + new LocaleList(englishLocale) : null; private final Context context; + private Object userLocales; private Locale userLocale; CompatConfigHelper(Context context) { @@ -32,22 +36,35 @@ final class CompatConfigHelper { void checkResourcesOverrides(Resources.Theme theme, boolean forceEnglish, Boolean nightModeOverride) { - final Resources res = theme.getResources(); - final Configuration conf = res.getConfiguration(); + Resources res = theme.getResources(); + if (this.checkResourcesOverrides(res.getConfiguration(), + forceEnglish, nightModeOverride)) { + res.updateConfiguration( + res.getConfiguration(), + res.getDisplayMetrics()); + } + } + + boolean checkResourcesOverrides(Configuration conf, boolean forceEnglish, + Boolean nightModeOverride) { Locale current = conf.locale; boolean didChange = false; - if (forceEnglish != current.equals(english)) { + boolean wasForceEnglish = englishLocale.equals(current); + if (forceEnglish != wasForceEnglish) { didChange = true; if (forceEnglish) { this.userLocale = conf.locale; - conf.locale = english; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - conf.setLocales(LocaleList.getEmptyLocaleList()); + this.userLocales = conf.getLocales(); + } + conf.locale = englishLocale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + conf.setLocales((LocaleList) englishLocales); } } else { conf.locale = this.userLocale; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - conf.setLocales(LocaleList.getAdjustedDefault()); + conf.setLocales((LocaleList) this.userLocales); } } } @@ -62,15 +79,16 @@ final class CompatConfigHelper { Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO; conf.uiMode = nightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK); } - if (didChange) { - res.updateConfiguration(conf, null); - if (!forceEnglish) this.userLocale = null; + if (!forceEnglish && !wasForceEnglish) { + this.userLocale = null; + this.userLocales = null; } + return didChange; } public Locale getUserLocale() { // Only use cached value if force english Locale locale = this.context.getResources().getConfiguration().locale; - return english.equals(locale) ? this.userLocale : locale; + return englishLocale.equals(locale) ? this.userLocale : locale; } } diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java index 149b301..f9b5fdd 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java @@ -57,6 +57,7 @@ public class InstallerActivity extends CompatActivity { Log.e(TAG, "Failed to mkdir module cache dir!"); super.onCreate(savedInstanceState); this.setDisplayHomeAsUpEnabled(true); + setActionBarBackground(null); this.setOnBackPressedCallback(a -> { this.canceled = true; return false; }); diff --git a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java index e123cc5..cdd3c80 100644 --- a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java +++ b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java @@ -43,8 +43,9 @@ public class MarkdownActivity extends CompatActivity { String config = intent.getExtras() .getString(Constants.EXTRA_MARKDOWN_CONFIG); if (title != null && !title.isEmpty()) { - setTitle(title); + this.setTitle(title); } + setActionBarBackground(null); this.getWindow().setFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index dfe0ad2..8b079c4 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -1,19 +1,23 @@ package com.fox2code.mmm.settings; import android.os.Bundle; +import android.view.View; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import androidx.recyclerview.widget.RecyclerView; import com.fox2code.mmm.AppUpdateManager; import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.Constants; import com.fox2code.mmm.MainApplication; +import com.fox2code.mmm.OverScrollManager; import com.fox2code.mmm.R; import com.fox2code.mmm.compat.CompatActivity; import com.fox2code.mmm.compat.CompatThemeWrapper; @@ -23,6 +27,8 @@ import com.fox2code.mmm.repo.RepoManager; import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.IntentHelper; import com.mikepenz.aboutlibraries.LibsBuilder; +import com.mikepenz.aboutlibraries.LibsConfiguration; +import com.mikepenz.aboutlibraries.ui.LibsSupportFragment; import com.topjohnwu.superuser.internal.UiThreadHandler; public class SettingsActivity extends CompatActivity { @@ -35,6 +41,7 @@ public class SettingsActivity extends CompatActivity { this.setDisplayHomeAsUpEnabled(true); setContentView(R.layout.settings_activity); setTitle(R.string.app_name); + setActionBarBackground(null); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() @@ -50,6 +57,7 @@ public class SettingsActivity extends CompatActivity { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName("mmm"); setPreferencesFromResource(R.xml.root_preferences, rootKey); + OverScrollManager.install(getListView()); findPreference("pref_manage_repos").setOnPreferenceClickListener(p -> { devModeStep = 0; openFragment(new RepoFragment(), R.string.manage_repos_pref); @@ -100,7 +108,8 @@ public class SettingsActivity extends CompatActivity { } final LibsBuilder libsBuilder = new LibsBuilder().withShowLoadingProgress(false) - .withLicenseShown(true).withAboutMinimalDesign(false); + .withLicenseShown(true).withAboutMinimalDesign(false) + .withUiListener(new OverScrollManager.LibsOverScroll()); Preference update = findPreference("pref_update"); update.setVisible(AppUpdateManager.getAppUpdateManager().peekHasUpdate()); update.setOnPreferenceClickListener(p -> { @@ -162,6 +171,7 @@ public class SettingsActivity extends CompatActivity { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { getPreferenceManager().setSharedPreferencesName("mmm"); setPreferencesFromResource(R.xml.repo_preferences, rootKey); + OverScrollManager.install(getListView()); setRepoData(RepoManager.MAGISK_ALT_REPO, "Magisk Modules Alt Repo", RepoManager.MAGISK_ALT_REPO_HOMEPAGE, null, null, diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8165540..2002bd0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -50,15 +50,15 @@ android:id="@+id/search_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" + android:layout_marginLeft="12dp" + android:layout_marginRight="12dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" - android:gravity="center_vertical" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" + android:gravity="right" + app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" - app:fitsSystemWindowsInsets="bottom"> + app:fitsSystemWindowsInsets="bottom" + tools:ignore="RtlHardcoded"> + + @color/transparent + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 26ab45f..ba10cbe 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,6 +9,8 @@ #FFA726 #80000000 #80FFFFFF + #00000000 #FF000000 #FFFFFFFF + @color/black_transparent \ 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 0cddfb5..f163125 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -4,7 +4,7 @@ tools:targetApi="q">true true - @color/white + @color/orange_200 @color/white @color/white @@ -12,7 +12,8 @@ @color/orange_200 @color/white - ?attr/colorPrimaryVariant + @color/status_bar_color + true true