Rewrite for better Blur, MultiWindow, and Notch/DisplayCutout support.

pull/27/head
Fox2Code 3 years ago
parent 738bd3f819
commit c027e743ce

@ -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

@ -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(

@ -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);
}

@ -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),

@ -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);

@ -16,11 +16,17 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_list"
<!-- FrameLayout is the best way to fix blurring -->
<FrameLayout
android:id="@+id/blur_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:edgeToEdge="true" />
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:edgeToEdge="true" />
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<eightbitlab.com.blurview.BlurView

Loading…
Cancel
Save