Rework user interface, add blur view, fix landscape.

pull/85/head
Fox2Code 2 years ago
parent 7a6aa28277
commit 2a97cbafa3

@ -3,17 +3,24 @@ 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;
import com.fox2code.mmm.compat.CompatActivity;
import com.fox2code.mmm.installer.InstallerInitializer;
@ -25,6 +32,9 @@ import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import eightbitlab.com.blurview.BlurView;
import eightbitlab.com.blurview.RenderScriptBlur;
public class MainActivity extends CompatActivity implements SwipeRefreshLayout.OnRefreshListener,
SearchView.OnQueryTextListener, SearchView.OnCloseListener {
private static final String TAG = "MainActivity";
@ -33,7 +43,11 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
public LinearProgressIndicator progressIndicator;
private ModuleViewAdapter moduleViewAdapter;
private SwipeRefreshLayout swipeRefreshLayout;
private int swipeRefreshLayoutOrigStartOffset;
private int swipeRefreshLayoutOrigEndOffset;
private long swipeRefreshBlocker = 0;
private TextView actionBarPadding;
private BlurView actionBarBlur;
private RecyclerView moduleList;
private CardView searchCard;
private SearchView searchView;
@ -55,10 +69,24 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
setContentView(R.layout.activity_main);
this.setTitle(R.string.app_name);
this.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams layoutParams = this.getWindow().getAttributes();
layoutParams.layoutInDisplayCutoutMode = // Support cutout in Android 9
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
this.getWindow().setAttributes(layoutParams);
}
this.actionBarPadding = findViewById(R.id.action_bar_padding);
this.actionBarBlur = findViewById(R.id.action_bar_blur);
this.progressIndicator = findViewById(R.id.progress_bar);
this.swipeRefreshLayout = findViewById(R.id.swipe_refresh);
this.swipeRefreshLayoutOrigStartOffset =
this.swipeRefreshLayout.getProgressViewStartOffset();
this.swipeRefreshLayoutOrigEndOffset =
this.swipeRefreshLayout.getProgressViewEndOffset();
this.swipeRefreshBlocker = Long.MAX_VALUE;
this.moduleList = findViewById(R.id.module_list);
this.searchCard = findViewById(R.id.search_card);
@ -68,6 +96,11 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
this.moduleList.setLayoutManager(new LinearLayoutManager(this));
this.moduleList.setItemViewCacheSize(4); // Default is 2
this.swipeRefreshLayout.setOnRefreshListener(this);
this.actionBarBlur.setupWith(this.moduleList).setFrameClearDrawable(
this.getWindow().getDecorView().getBackground())
.setBlurAlgorithm(new RenderScriptBlur(this))
.setBlurRadius(5F).setBlurAutoUpdate(true)
.setHasFixedTransformationMatrix(true);
this.moduleList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
@ -158,6 +191,7 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
progressIndicator.setProgressCompat(PRECISION, true);
progressIndicator.setVisibility(View.GONE);
searchView.setEnabled(true);
setActionBarBackground(null);
});
moduleViewListBuilder.appendRemoteModules();
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
@ -175,12 +209,29 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
TypedValue value = new TypedValue();
theme.resolveAttribute(backgroundAttr, value, true);
this.searchCard.setCardBackgroundColor(value.data);
this.searchCard.setAlpha(iconified ? 0.70F : 1F);
this.searchCard.setAlpha(iconified ? 0.80F : 1F);
}
private void updateScreenInsets() {
this.moduleViewListBuilder.setFooterPx(
this.getNavigationBarHeight() + this.searchCard.getHeight());
this.runOnUiThread(() -> this.updateScreenInsets(
this.getResources().getConfiguration()));
}
private void updateScreenInsets(Configuration configuration) {
boolean landscape = configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE;
int statusBarHeight = getStatusBarHeight();
int actionBarHeight = getActionBarHeight();
int combinedBarsHeight = statusBarHeight + actionBarHeight;
this.actionBarPadding.setMinHeight(combinedBarsHeight);
this.swipeRefreshLayout.setProgressViewOffset(false,
swipeRefreshLayoutOrigStartOffset + combinedBarsHeight,
swipeRefreshLayoutOrigEndOffset + combinedBarsHeight);
this.moduleViewListBuilder.setHeaderPx(actionBarHeight);
this.moduleViewListBuilder.setFooterPx((landscape ? 0 :
this.getNavigationBarHeight()) + this.searchCard.getHeight());
this.moduleViewListBuilder.updateInsets();
this.actionBarBlur.invalidate();
}
@Override
@ -234,6 +285,12 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
this.initMode = false;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
this.updateScreenInsets(newConfig);
super.onConfigurationChanged(newConfig);
}
@Override
public void onRefresh() {
if (this.swipeRefreshBlocker > System.currentTimeMillis() ||

@ -24,7 +24,7 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
public final String moduleId;
public final NotificationType notificationType;
public final Type separator;
public final int footerPx;
public int footerPx;
public View.OnClickListener onClickListener;
public LocalModuleInfo moduleInfo;
public RepoModule repoModule;
@ -34,32 +34,33 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
this.moduleId = Objects.requireNonNull(moduleId);
this.notificationType = null;
this.separator = null;
this.footerPx = 0;
this.footerPx = -1;
}
public ModuleHolder(NotificationType notificationType) {
this.moduleId = "";
this.notificationType = Objects.requireNonNull(notificationType);
this.separator = null;
this.footerPx = 0;
this.footerPx = -1;
}
public ModuleHolder(Type separator) {
this.moduleId = "";
this.notificationType = null;
this.separator = separator;
this.footerPx = 0;
this.footerPx = -1;
}
public ModuleHolder(int footerPx) {
public ModuleHolder(int footerPx,boolean header) {
this.moduleId = "";
this.notificationType = null;
this.separator = null;
this.footerPx = footerPx;
this.filterLevel = header ? 1 : 0;
}
public boolean isModuleHolder() {
return this.notificationType == null && this.separator == null && this.footerPx == 0;
return this.notificationType == null && this.separator == null && this.footerPx == -1;
}
public ModuleInfo getMainModuleInfo() {
@ -115,7 +116,7 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
}
public Type getType() {
if (this.footerPx != 0) {
if (this.footerPx != -1) {
return Type.FOOTER;
} else if (this.separator != null) {
return Type.SEPARATOR;
@ -145,7 +146,7 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
public boolean shouldRemove() {
return this.notificationType != null ? this.notificationType.shouldRemove() :
this.footerPx == 0 && this.moduleInfo == null &&
this.footerPx == -1 && this.moduleInfo == null &&
(this.repoModule == null || !this.repoModule.repoData.isEnabled() ||
(PropUtils.isLowQualityModule(this.repoModule.moduleInfo) &&
!MainApplication.isDisableLowQualityModuleFilter()));
@ -207,6 +208,7 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
}
public enum Type implements Comparator<ModuleHolder> {
HEADER(R.string.loading, false, false),
SEPARATOR(R.string.loading, false, false) {
@Override
@SuppressWarnings("ConstantConditions")

@ -26,6 +26,7 @@ import com.google.android.material.switchmaterial.SwitchMaterial;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import java.util.ArrayList;
import java.util.Objects;
public final class ModuleViewAdapter extends RecyclerView.Adapter<ModuleViewAdapter.ViewHolder> {
private static final boolean DEBUG = false;
@ -182,14 +183,16 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter<ModuleViewAdap
if (localModuleInfo == null || moduleInfo.versionCode >
localModuleInfo.updateVersionCode) {
this.creditText.setText((localModuleInfo == null ||
moduleInfo.version.equals(localModuleInfo.version) ?
Objects.equals(moduleInfo.version, localModuleInfo.version) ?
moduleInfo.version : localModuleInfo.version + " (" +
this.getString(R.string.module_last_update) +
moduleInfo.version + ")") + " " +
this.getString(R.string.module_by) + " " + moduleInfo.author);
} else {
this.creditText.setText(localModuleInfo.version + (
localModuleInfo.version.equals(localModuleInfo.updateVersion) ?
(localModuleInfo.updateVersion != null &&
Objects.equals(localModuleInfo.version,
localModuleInfo.updateVersion)) ?
"" : " (" + this.getString(R.string.module_last_update) +
localModuleInfo.updateVersion + ")") + " " +
this.getString(R.string.module_by) + " " + localModuleInfo.author);

@ -23,6 +23,7 @@ import java.util.Locale;
public class ModuleViewListBuilder {
private static final String TAG = "ModuleViewListBuilder";
private static final Runnable RUNNABLE = () -> {};
private final EnumSet<NotificationType> notifications = EnumSet.noneOf(NotificationType.class);
private final HashMap<String, ModuleHolder> mappedModuleHolders = new HashMap<>();
private final Object updateLock = new Object();
@ -31,8 +32,10 @@ public class ModuleViewListBuilder {
@NonNull
private String query = "";
private boolean updating;
private int headerPx;
private int footerPx;
private ModuleSorter moduleSorter = ModuleSorter.UPDATE;
private Runnable updateInsets = RUNNABLE;
public ModuleViewListBuilder(Activity activity) {
this.activity = activity;
@ -100,6 +103,7 @@ public class ModuleViewListBuilder {
this.updating = true;
final ArrayList<ModuleHolder> moduleHolders;
final int newNotificationsLen;
final ModuleHolder[] headerFooter = new ModuleHolder[2];
try {
synchronized (this.updateLock) {
// Build start
@ -150,9 +154,12 @@ public class ModuleViewListBuilder {
}
}
Collections.sort(moduleHolders, this.moduleSorter);
if (this.footerPx != 0) { // Footer is always last
moduleHolders.add(new ModuleHolder(this.footerPx));
}
// Header is always first
moduleHolders.add(0, headerFooter[0] =
new ModuleHolder(this.headerPx, true));
// Footer is always last
moduleHolders.add(headerFooter[1] =
new ModuleHolder(this.footerPx, false));
Log.i(TAG, "Got " + moduleHolders.size() + " entries!");
// Build end
}
@ -160,6 +167,7 @@ public class ModuleViewListBuilder {
this.updating = false;
}
this.activity.runOnUiThread(() -> {
this.updateInsets = RUNNABLE;
final EnumSet<NotificationType> oldNotifications =
EnumSet.noneOf(NotificationType.class);
boolean isTop = !moduleList.canScrollVertically(-1);
@ -172,7 +180,8 @@ public class ModuleViewListBuilder {
oldNotifications.add(notificationType);
if (!notificationType.special)
oldNotificationsLen++;
}
} else if (moduleHolder.footerPx != -1)
oldNotificationsLen++; // Fix header
if (moduleHolder.separator == ModuleHolder.Type.INSTALLABLE)
break;
oldOfflineModulesLen++;
@ -208,6 +217,13 @@ public class ModuleViewListBuilder {
}
if (isTop) moduleList.scrollToPosition(0);
if (isBottom) moduleList.scrollToPosition(newLen);
this.updateInsets = () -> {
headerFooter[0].footerPx = this.headerPx;
headerFooter[1].footerPx = this.footerPx;
notifySizeChanged(moduleViewAdapter, 0, 1, 1);
notifySizeChanged(moduleViewAdapter,
moduleHolders.size(), 1, 1);
};
});
}
@ -281,6 +297,14 @@ public class ModuleViewListBuilder {
return true;
}
public void setHeaderPx(int headerPx) {
if (this.headerPx != headerPx) {
synchronized (this.updateLock) {
this.headerPx = headerPx;
}
}
}
public void setFooterPx(int footerPx) {
if (this.footerPx != footerPx) {
synchronized (this.updateLock) {
@ -288,4 +312,8 @@ public class ModuleViewListBuilder {
}
}
}
public void updateInsets() {
this.updateInsets.run();
}
}

@ -165,13 +165,17 @@ public class AndroidacyWebAPI {
/**
* Show action bar if not visible, the action bar is only visible by default on notes.
* Optional title param to set action bar title.
*/
@JavascriptInterface
public void showActionBar() {
public void showActionBar(final String title) {
if (this.consumedAction) return;
this.consumedAction = true;
this.activity.runOnUiThread(() -> {
this.activity.showActionBar();
if (title != null && !title.isEmpty()) {
this.activity.setTitle(title);
}
this.consumedAction = false;
});
}

@ -6,6 +6,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@ -198,6 +199,35 @@ public class CompatActivity extends AppCompatActivity {
}
}
public void setActionBarBackground(Drawable drawable) {
androidx.appcompat.app.ActionBar compatActionBar;
try {
compatActionBar = this.getSupportActionBar();
} catch (Exception e) {
Log.e(TAG, "Failed to call getSupportActionBar", e);
compatActionBar = null; // Allow fallback to builtin actionBar.
}
if (compatActionBar != null) {
compatActionBar.setBackgroundDrawable(drawable);
} else {
android.app.ActionBar actionBar = this.getActionBar();
if (actionBar != null)
actionBar.setBackgroundDrawable(drawable);
}
}
@Dimension @Px
public int getStatusBarHeight() { // How to improve this?
int height = WindowInsetsCompat.CONSUMED.getInsets(
WindowInsetsCompat.Type.statusBars()).top;
if (height == 0) { // Fallback to system resources
int id = Resources.getSystem().getIdentifier(
"status_bar_height", "dimen", "android");
if (id > 0) return Resources.getSystem().getDimensionPixelSize(id);
}
return height;
}
public int getNavigationBarHeight() { // How to improve this?
int height = WindowInsetsCompat.CONSUMED.getInsets(
WindowInsetsCompat.Type.navigationBars()).bottom;

@ -2,9 +2,10 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fitsSystemWindowsInsets="top"
app:fitsSystemWindowsInsets="left|right"
tools:context=".MainActivity">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
@ -19,9 +20,23 @@
android:id="@+id/module_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
app:edgeToEdge="true" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<eightbitlab.com.blurview.BlurView
android:id="@+id/action_bar_blur"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/action_bar_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</eightbitlab.com.blurview.BlurView>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"
android:layout_height="wrap_content"
@ -29,7 +44,7 @@
android:indeterminate="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />
<LinearLayout
android:id="@+id/search_container"
@ -57,7 +72,8 @@
<androidx.appcompat.widget.SearchView
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:background="@null" />
</androidx.cardview.widget.CardView>
</LinearLayout>

@ -17,6 +17,7 @@
android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/card_corner_radius"
app:strokeColor="@android:color/transparent"
app:cardPreventCornerOverlap="true"
app:strokeWidth="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

@ -7,6 +7,8 @@
<color name="teal_700">#FF018786</color>
<color name="orange_700">#EF6C00</color>
<color name="orange_200">#FFA726</color>
<color name="black_transparent">#80000000</color>
<color name="white_transparent">#80FFFFFF</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>
Loading…
Cancel
Save