From c027e743ce8f5bfde40a8dc54d218164b9b64222 Mon Sep 17 00:00:00 2001 From: Fox2Code Date: Sun, 3 Jul 2022 20:51:04 +0200 Subject: [PATCH] Rewrite for better Blur, MultiWindow, and Notch/DisplayCutout support. --- .../java/com/fox2code/mmm/MainActivity.java | 34 +++--- .../fox2code/mmm/compat/CompatActivity.java | 103 +++++++++++++----- .../com/fox2code/mmm/compat/CompatNotch.java | 11 +- .../mmm/markdown/MarkdownActivity.java | 4 + .../mmm/settings/SettingsActivity.java | 5 +- app/src/main/res/layout/activity_main.xml | 14 ++- 6 files changed, 119 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 9cd3291..33f3106 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -1,13 +1,5 @@ package com.fox2code.mmm; -import androidx.annotation.NonNull; -import androidx.appcompat.widget.SearchView; -import androidx.cardview.widget.CardView; -import androidx.core.graphics.ColorUtils; -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.Color; @@ -21,6 +13,14 @@ import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.SearchView; +import androidx.cardview.widget.CardView; +import androidx.core.graphics.ColorUtils; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.fox2code.mmm.compat.CompatActivity; import com.fox2code.mmm.compat.CompatDisplay; import com.fox2code.mmm.installer.InstallerInitializer; @@ -32,8 +32,6 @@ import com.fox2code.mmm.repo.RepoManager; import com.fox2code.mmm.settings.SettingsActivity; import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.IntentHelper; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.progressindicator.LinearProgressIndicator; import eightbitlab.com.blurview.BlurView; @@ -105,12 +103,11 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O this.moduleList.setItemViewCacheSize(4); // Default is 2 this.swipeRefreshLayout.setOnRefreshListener(this); this.actionBarBlur.setBackground(this.actionBarBackground); - this.actionBarBlur.setupWith(this.moduleList).setFrameClearDrawable( - this.getWindow().getDecorView().getBackground()) - .setBlurAlgorithm(new RenderScriptBlur(this)) - .setBlurRadius(4F).setBlurAutoUpdate(true) - .setHasFixedTransformationMatrix(true); - this.updateBlurState(); + this.actionBarBlur.setupWith(findViewById(R.id.blur_frame)) + .setFrameClearDrawable(this.getWindow().getDecorView().getBackground()) + .setBlurAlgorithm(new RenderScriptBlur(this)).setBlurRadius(4F) + .setBlurAutoUpdate(true).setHasFixedTransformationMatrix(true); + this.updateBlurState(); this.moduleList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { @@ -338,9 +335,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O } @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - this.updateScreenInsets(newConfig); - super.onConfigurationChanged(newConfig); + protected void onWindowUpdated() { + this.updateScreenInsets(); } @Override 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 3ca6b77..3f61923 100644 --- a/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java +++ b/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java @@ -11,6 +11,8 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.os.SystemProperties; import android.util.Log; import android.util.TypedValue; @@ -33,12 +35,14 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.core.widget.NestedScrollView; import androidx.fragment.app.Fragment; @@ -61,6 +65,7 @@ import rikka.layoutinflater.view.LayoutInflaterFactory; * I will probably outsource this to a separate library later */ public class CompatActivity extends AppCompatActivity { + private static final Handler handler = new Handler(Looper.getMainLooper()); public static final int INTENT_ACTIVITY_REQUEST_CODE = 0x01000000; private static final String TAG = "CompatActivity"; public static final CompatActivity.OnBackPressedCallback DISABLE_BACK_BUTTON = @@ -80,20 +85,37 @@ public class CompatActivity extends AppCompatActivity { private int displayCutoutHeight = 0; @Rotation private int cachedRotation = 0; @StyleRes private int setThemeDynamic = 0; + private boolean awaitOnWindowUpdate = false; private boolean onCreateCalledOnce = false; private boolean onCreateCalled = false; private boolean isRefreshUi = false; private boolean hasHardwareNavBar; private int drawableResId; private MenuItem menuItem; - // CompatConfigHelper - private boolean forceEnglish; - private Boolean nightModeOverride; public CompatActivity() { this.selfReference = new WeakReference<>(this); } + void postWindowUpdated() { + if (this.awaitOnWindowUpdate) return; + this.awaitOnWindowUpdate = true; + handler.post(() -> { + this.awaitOnWindowUpdate = false; + if (this.isFinishing()) return; + this.cachedRotation = this.getRotation(); + this.displayCutoutHeight = CompatNotch.getNotchHeight(this); + this.onWindowUpdated(); + }); + } + + /** + * Function to detect when Window state is updated + * */ + protected void onWindowUpdated() { + // No-op + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { if (!this.onCreateCalled) { @@ -125,8 +147,6 @@ public class CompatActivity extends AppCompatActivity { @Override protected void onResume() { this.hasHardwareNavBar = this.hasHardwareNavBar0(); - this.displayCutoutHeight = CompatNotch.getNotchHeight(this); - this.cachedRotation = this.getRotation(); super.onResume(); this.refreshUI(); } @@ -135,12 +155,19 @@ public class CompatActivity extends AppCompatActivity { public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); if (this.cachedRotation != this.getRotation() && - this.onCreateCalledOnce) { + this.onCreateCalledOnce && !this.awaitOnWindowUpdate) { this.cachedRotation = this.getRotation(); this.displayCutoutHeight = CompatNotch.getNotchHeight(this); + this.onWindowUpdated(); } } + @Override @CallSuper @RequiresApi(Build.VERSION_CODES.N) + public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { + super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); + this.postWindowUpdated(); + } + @Override public void finish() { this.onActivityResultCallback = null; @@ -157,15 +184,18 @@ public class CompatActivity extends AppCompatActivity { public void refreshUI() { // Avoid recursive calls if (this.isRefreshUi || !this.onCreateCalled) return; - Application application = this.getApplication(); - if (application instanceof ApplicationCallbacks) { - this.isRefreshUi = true; - try { + this.isRefreshUi = true; + try { + this.cachedRotation = this.getRotation(); + this.displayCutoutHeight = CompatNotch.getNotchHeight(this); + Application application = this.getApplication(); + if (application instanceof ApplicationCallbacks) { ((ApplicationCallbacks) application) .onRefreshUI(this); - } finally { - this.isRefreshUi = false; } + this.postWindowUpdated(); + } finally { + this.isRefreshUi = false; } } @@ -295,6 +325,20 @@ public class CompatActivity extends AppCompatActivity { } } + public boolean isActivityWindowed() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + (super.isInMultiWindowMode() || super.isInPictureInPictureMode()); + } + + @Nullable + public WindowInsetsCompat getWindowInsets() { + View view = findViewById(android.R.id.content); + return view != null ? ViewCompat.getRootWindowInsets(view) : null; + } + + /** + * @return Activity status bar height, may be 0 if not affecting the activity. + */ @Dimension @Px @SuppressLint("InternalInsetResource") public int getStatusBarHeight() { @@ -302,32 +346,41 @@ public class CompatActivity extends AppCompatActivity { int height = this.getRotation() == 0 ? this.displayCutoutHeight : 0; // Check consumed insets - if (WindowInsetsCompat.CONSUMED.isConsumed()) { - Insets insets = WindowInsetsCompat.CONSUMED.getInsets( + boolean windowed = this.isActivityWindowed(); + WindowInsetsCompat windowInsetsCompat = this.getWindowInsets(); + if (windowInsetsCompat != null || windowed) { + if (windowInsetsCompat == null) // Fallback for windowed mode + windowInsetsCompat = WindowInsetsCompat.CONSUMED; + Insets insets = windowInsetsCompat.getInsets( WindowInsetsCompat.Type.statusBars()); - if (insets.top == 0) { - height = Math.max(height, insets.bottom); - } + if (windowed) return Math.max(insets.top, 0); + height = Math.max(height, insets.top); } // Check system resources int id = Resources.getSystem().getIdentifier( - "status_bar_height", "dimen", "android"); + "status_bar_height_default", "dimen", "android"); + if (id <= 0) { + id = Resources.getSystem().getIdentifier( + "status_bar_height", "dimen", "android"); + } return id <= 0 ? height : Math.max(height, Resources.getSystem().getDimensionPixelSize(id)); } + /** + * @return Activity status bar height, may be 0 if not affecting the activity. + */ @Dimension @Px @SuppressLint("InternalInsetResource") public int getNavigationBarHeight() { int height = 0; // Check consumed insets - if (WindowInsetsCompat.CONSUMED.isConsumed()) { - Insets insets = WindowInsetsCompat.CONSUMED.getInsets( - WindowInsetsCompat.Type.statusBars()); - if (insets.top != 0) { - height = Math.max(height, - insets.bottom - insets.top); - } + WindowInsetsCompat windowInsetsCompat = this.getWindowInsets(); + if (windowInsetsCompat != null) { + // Note: isActivityWindowed does not affect layout + Insets insets = windowInsetsCompat.getInsets( + WindowInsetsCompat.Type.navigationBars()); + height = Math.max(height, insets.bottom); } // Check system resources int id = Resources.getSystem().getIdentifier( diff --git a/app/src/main/java/com/fox2code/mmm/compat/CompatNotch.java b/app/src/main/java/com/fox2code/mmm/compat/CompatNotch.java index a83b557..cffd2ad 100644 --- a/app/src/main/java/com/fox2code/mmm/compat/CompatNotch.java +++ b/app/src/main/java/com/fox2code/mmm/compat/CompatNotch.java @@ -2,6 +2,7 @@ package com.fox2code.mmm.compat; import android.annotation.SuppressLint; import android.content.res.Resources; +import android.graphics.Insets; import android.os.Build; import android.os.SystemProperties; import android.util.Log; @@ -38,7 +39,15 @@ final class CompatNotch { private static int getNotchHeightModern(CompatActivity compatActivity) { Display display = compatActivity.getDisplay(); DisplayCutout displayCutout = display == null ? null : display.getCutout(); - if (displayCutout != null) return Math.max(displayCutout.getSafeInsetTop(), 1); + if (displayCutout != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Insets insets = displayCutout.getWaterfallInsets(); + return Math.max(displayCutout.getSafeInsetTop(), + insets == null || insets.top == 0 ? 1 : insets.top); + } else { + return Math.max(displayCutout.getSafeInsetTop(), 1); + } + } DisplayCutoutCompat displayCutoutCompat = WindowInsetsCompat.CONSUMED.getDisplayCutout(); return displayCutoutCompat == null ? 0 : Math.max(displayCutoutCompat.getSafeInsetTop(), 1); } 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 12f96ad..c3ec71a 100644 --- a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java +++ b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java @@ -239,6 +239,10 @@ public class MarkdownActivity extends CompatActivity { this.updateBlurState(); } + @Override + protected void onWindowUpdated() { + this.updateScreenInsets(); + } private void addChip(MarkdownChip markdownChip) { this.makeChip(this.getString(markdownChip.title), 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 a34d404..4e460ff 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -23,7 +23,6 @@ import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.Constants; import com.fox2code.mmm.MainActivity; 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.installer.InstallerInitializer; @@ -31,7 +30,6 @@ import com.fox2code.mmm.repo.RepoData; import com.fox2code.mmm.repo.RepoManager; import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.IntentHelper; - import com.fox2code.rosettax.LanguageActivity; import com.fox2code.rosettax.LanguageSwitcher; import com.mikepenz.aboutlibraries.LibsBuilder; @@ -59,11 +57,12 @@ public class SettingsActivity extends CompatActivity implements LanguageActivity } @Override + @SuppressLint("InlinedApi") public void refreshRosettaX() { Intent mStartActivity = new Intent(this, MainActivity.class); mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); int mPendingIntentId = 123456; - @SuppressLint("InlinedApi") PendingIntent mPendingIntent = PendingIntent.getActivity(this, mPendingIntentId, + PendingIntent mPendingIntent = PendingIntent.getActivity(this, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 405847d..8da9a8c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -16,11 +16,17 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" > - + + android:layout_height="match_parent"> + +