Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 292 additions & 25 deletions app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class SpecialViewHolder(
// if(utilsProvider.getAppTheme().equals(AppTheme.DARK))
// view.setBackgroundResource(R.color.holo_dark_background);
if (utilsProvider.appTheme == AppTheme.LIGHT) {
txtTitle.setTextColor(Utils.getColor(c, R.color.text_light))
txtTitle.setTextColor(Utils.getColor(c, R.color.section_header_light))
} else {
txtTitle.setTextColor(Utils.getColor(c, R.color.text_dark))
txtTitle.setTextColor(Utils.getColor(c, R.color.section_header_dark))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.usb.UsbManager;
import android.media.RingtoneManager;
import android.net.Uri;
Expand All @@ -197,6 +199,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.arch.core.util.Function;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
Expand Down Expand Up @@ -1077,6 +1080,8 @@ public void goToMain(String path, boolean hideFab) {
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.activity_extra, menu);
// Tint the search / view / overflow icons to sit on the app bar surface.
tintMenuIcons(menu, getOnAppBarColor());
/*
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
Expand Down Expand Up @@ -1832,6 +1837,76 @@ public void updateViews(ColorDrawable colorDrawable) {
}
}

/**
* Recolours the top app bar chrome (toolbar, status bar, path breadcrumb) to a surface colour —
* white in light mode, an elevated grey in dark/black — instead of the primary colour, and lifts
* it with a small elevation so it reads as sitting above the file listing. The drawer header
* keeps the primary colour. Re-applied after action mode exits so selection mode can still
* recolour the bar. Scoped to this activity's shared toolbar; other activities are unaffected.
*/
public void applyAppBarSurface() {
if (appbar == null) {
return;
}
final int surface = getAppBarSurfaceColor();
final int onSurface = getOnAppBarColor();
final Toolbar toolbar = getAppbar().getToolbar();
final AppBarLayout appBarLayout = getAppbar().getAppbarLayout();

appBarLayout.setBackgroundColor(surface);
toolbar.setBackgroundColor(surface);
if (SDK_INT >= LOLLIPOP) {
// Constant elevation (drop the scroll-driven lift) so the bar always casts a shadow.
appBarLayout.setStateListAnimator(null);
appBarLayout.setElevation(getResources().getDimension(R.dimen.app_bar_elevation));
}

toolbar.setTitleTextColor(onSurface);
toolbar.setSubtitleTextColor(onSurface);
tintDrawable(toolbar.getNavigationIcon(), onSurface);
tintDrawable(toolbar.getOverflowIcon(), onSurface);
tintMenuIcons(toolbar.getMenu(), onSurface);

getAppbar().getBottomBar().setBackgroundColor(surface);
getAppbar().getBottomBar().setPathTextColor(onSurface);

if (SDK_INT >= LOLLIPOP) {
getWindow().setStatusBarColor(surface);
}
}

/** App bar surface colour for the current theme (white / elevated grey / near-black). */
public int getAppBarSurfaceColor() {
if (getAppTheme().equals(AppTheme.LIGHT)) {
return Utils.getColor(this, R.color.app_bar_light);
} else if (getAppTheme().equals(AppTheme.BLACK)) {
return Utils.getColor(this, R.color.app_bar_black);
}
return Utils.getColor(this, R.color.app_bar_dark);
}

/** Title / icon / path colour that reads on {@link #getAppBarSurfaceColor()}. */
public int getOnAppBarColor() {
return getAppTheme().equals(AppTheme.LIGHT)
? Utils.getColor(this, R.color.on_app_bar_light)
: Utils.getColor(this, R.color.on_app_bar_dark);
}

private void tintDrawable(@Nullable Drawable drawable, int color) {
if (drawable != null) {
drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
}

void tintMenuIcons(@Nullable Menu menu, int color) {
if (menu == null) {
return;
}
for (int i = 0; i < menu.size(); i++) {
tintDrawable(menu.getItem(i).getIcon(), color);
}
}

void initialiseFab() {
int colorAccent = getAccent();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
Expand All @@ -121,6 +122,7 @@
import androidx.core.content.ContextCompat;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
Expand Down Expand Up @@ -253,11 +255,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

mSwipeRefreshLayout.setOnRefreshListener(() -> updateList(true));

// String itemsstring = res.getString(R.string.items);// TODO: 23/5/2017 use or delete
mToolbarContainer.setBackgroundColor(
MainActivity.currentTab == 1
? mainFragmentViewModel.getPrimaryTwoColor()
: mainFragmentViewModel.getPrimaryColor());
// The app bar is a white/grey surface that sits above the listing rather than the primary
// colour; MainActivity owns the recolour so the shared toolbar stays consistent.
requireMainActivity().applyAppBarSurface();

// listView.setPadding(listView.getPaddingLeft(), paddingTop, listView.getPaddingRight(),
// listView.getPaddingBottom());
Expand All @@ -268,11 +268,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
f.generateMode(getActivity());
getMainActivity().getAppbar().getBottomBar().setClickListener();

if (utilsProvider.getAppTheme().equals(AppTheme.LIGHT) && !mainFragmentViewModel.isList()) {
listView.setBackgroundColor(Utils.getColor(getContext(), R.color.grid_background_light));
} else {
listView.setBackgroundDrawable(null);
}
applyListViewBackground();
listView.setHasFixedSize(true);
if (mainFragmentViewModel.isList()) {
mLayoutManager = new CustomScrollLinearLayoutManager(getContext());
Expand All @@ -291,7 +287,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
// 23/5/2017 use or delete
dividerItemDecoration =
new DividerItemDecoration(requireActivity(), false, getBoolean(PREFERENCE_SHOW_DIVIDERS));
listView.addItemDecoration(dividerItemDecoration);
// Only list view uses row dividers; grid folders carry their own dividers inside the unified
// section container, so keep the decoration off the grid.
if (mainFragmentViewModel.isList()) {
listView.addItemDecoration(dividerItemDecoration);
}
mSwipeRefreshLayout.setColorSchemeColors(mainFragmentViewModel.getAccentColor());
DefaultItemAnimator animator = new DefaultItemAnimator();
listView.setItemAnimator(animator);
Expand Down Expand Up @@ -339,14 +339,61 @@ public int getSpanSize(int position) {
});
}

void switchToGrid() {
mainFragmentViewModel.setList(false);
/**
* Applies the file-listing page background that sits behind the section cards, honouring the
* current app theme. Centralised so the initial load and list/grid switches stay consistent, and
* so future background work (patterned "graffiti" surface) has a single hook.
*/
private void applyListViewBackground() {
final AppTheme theme = utilsProvider.getAppTheme();
final int pageColor;
final int patternRes;
// Tint strength: darker themes need a touch more of the colour to read as a tint.
final float tintFraction;
if (theme.equals(AppTheme.LIGHT)) {
pageColor = Utils.getColor(getContext(), R.color.page_background_light);
patternRes = R.drawable.bg_file_pattern_light;
tintFraction = 0.12f;
} else if (theme.equals(AppTheme.BLACK)) {
pageColor = Color.BLACK;
patternRes = R.drawable.bg_file_pattern_dark;
tintFraction = 0.08f;
} else {
pageColor = Utils.getColor(getContext(), R.color.page_background_dark);
patternRes = R.drawable.bg_file_pattern_dark;
tintFraction = 0.08f;
}

if (utilsProvider.getAppTheme().equals(AppTheme.LIGHT)) {
// Mirrors the drawer banner: a wash of the user's primary (app bar) colour over the page
// surface, with the tilted, translucent graffiti pattern on top. The list itself is transparent
// so this shows through the gaps around the section cards.
final int primaryColor =
MainActivity.currentTab == 1
? mainFragmentViewModel.getPrimaryTwoColor()
: mainFragmentViewModel.getPrimaryColor();
final int tintedBase = ColorUtils.blendARGB(pageColor, primaryColor, tintFraction);

// will always be grid, set alternate white background
listView.setBackgroundColor(Utils.getColor(getContext(), R.color.grid_background_light));
final View listBackground = rootView.findViewById(R.id.list_background);
if (listBackground != null) {
listBackground.setBackgroundColor(tintedBase);
}
final ImageView listBackgroundPattern = rootView.findViewById(R.id.list_background_pattern);
if (listBackgroundPattern != null) {
listBackgroundPattern.setImageResource(patternRes);
}
listView.setBackgroundColor(Color.TRANSPARENT);

// Left/right gutter so the section cards float with breathing room and the patterned surface
// shows through the edges. Applied on the list rather than per-row so folders/files stay flush.
final int gutter = (int) getResources().getDimension(R.dimen.list_card_horizontal_margin);
listView.setPadding(gutter, listView.getPaddingTop(), gutter, listView.getPaddingBottom());
listView.setClipToPadding(false);
}

void switchToGrid() {
mainFragmentViewModel.setList(false);

applyListViewBackground();

if (mLayoutManagerGrid == null)
if (mainFragmentViewModel.getColumns() == -1 || mainFragmentViewModel.getColumns() == 0)
Expand All @@ -365,10 +412,7 @@ void switchToGrid() {
void switchToList() {
mainFragmentViewModel.setList(true);

if (utilsProvider.getAppTheme().equals(AppTheme.LIGHT)) {

listView.setBackgroundDrawable(null);
}
applyListViewBackground();

if (mLayoutManager == null) mLayoutManager = new CustomScrollLinearLayoutManager(getActivity());
listView.setLayoutManager(mLayoutManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,9 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse
@ColorInt
int color = (int) evaluator.evaluate(position + positionOffset, startColor, endColor);

colorDrawable.setColor(color);
requireMainActivity().updateViews(colorDrawable);
// The app bar is a fixed white/grey surface now; only the drawer header follows the per-tab
// colour as panes are swiped, so the toolbar no longer flashes back to the primary colour.
requireMainActivity().getDrawer().setBackgroundColor(color);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package com.amaze.filemanager.ui.fragments.data

import android.content.SharedPreferences
import android.content.res.Resources
import android.os.Bundle
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
Expand Down Expand Up @@ -155,7 +156,22 @@ class MainFragmentViewModel : ViewModel() {
PREFERENCE_GRID_COLUMNS_DEFAULT,
)
Objects.requireNonNull(columnPreference)
columns = columnPreference?.toInt()
// Clamp to what the device can practically show, so grid cells never become too narrow to
// read (e.g. the default "3" on a phone, or a "6" saved before the cap existed).
columns = columnPreference?.toInt()?.coerceAtMost(maxGridColumns())
}

companion object {
/**
* Maximum number of grid columns that is practical on this device: phones cap at 2, tablets
* scale up to 4 based on the smallest screen width. Used to clamp both the stored preference
* and the options offered in settings.
*/
@JvmStatic
fun maxGridColumns(): Int {
val smallestWidthDp = Resources.getSystem().configuration.smallestScreenWidthDp
return (smallestWidthDp / 200).coerceIn(2, 4)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,14 @@ import android.os.Bundle
import androidx.preference.Preference
import com.afollestad.materialdialogs.MaterialDialog
import com.amaze.filemanager.R
import com.amaze.filemanager.ui.fragments.data.MainFragmentViewModel
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_GRID_COLUMNS
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_GRID_COLUMNS_DEFAULT
import com.amaze.filemanager.ui.theme.AppThemePreference
import java.util.Objects

class AppearancePrefsFragment : BasePrefsFragment() {
override val title = R.string.appearance

/**
* The actual value saved for the preference, to see the localized strings see [R.array.columns]
*/
private val savedPreferenceValues =
listOf(
PREFERENCE_GRID_COLUMNS_DEFAULT,
"2",
"3",
"4",
"5",
"6",
)
private var currentTheme = 0
private var gridColumnPref: Preference? = null

Expand Down Expand Up @@ -73,41 +61,35 @@ class AppearancePrefsFragment : BasePrefsFragment() {

private val onClickGridColumn =
Preference.OnPreferenceClickListener {
val dialog =
MaterialDialog.Builder(activity).also { builder ->
// Offer only the column counts this device can practically show: "Automatic" plus
// 2..maxGridColumns (2 on phones, up to 4 on tablets).
val maxColumns = MainFragmentViewModel.maxGridColumns()
val savedValues =
listOf(PREFERENCE_GRID_COLUMNS_DEFAULT) + (2..maxColumns).map { it.toString() }
val labels: List<CharSequence> =
listOf(getString(R.string.default_string)) + (2..maxColumns).map { it.toString() }

val saved =
activity.prefs.getString(PREFERENCE_GRID_COLUMNS, PREFERENCE_GRID_COLUMNS_DEFAULT)
val current = savedValues.indexOf(saved).coerceAtLeast(0)

MaterialDialog.Builder(activity)
.also { builder ->
builder.theme(activity.utilsProvider.appTheme.getMaterialDialogTheme())
builder.title(R.string.gridcolumnno)
val columnsPreference =
activity
.prefs
.getString(PREFERENCE_GRID_COLUMNS, PREFERENCE_GRID_COLUMNS_DEFAULT)

Objects.requireNonNull(columnsPreference)
val current =
when (columnsPreference) {
null -> {
PREFERENCE_GRID_COLUMNS_DEFAULT.toInt()
}
else -> {
columnsPreference.toInt() - 1
}
}

builder
.items(R.array.columns)
.items(labels)
.itemsCallbackSingleChoice(current) { dialog, _, which, _ ->
val editor = activity.prefs.edit()
editor.putString(
PREFERENCE_GRID_COLUMNS,
savedPreferenceValues[which],
)
editor.apply()
activity.prefs
.edit()
.putString(PREFERENCE_GRID_COLUMNS, savedValues[which])
.apply()
dialog.dismiss()
updateGridColumnSummary()
true
}
}.build()
dialog.show()
.show()

true
}
Expand Down Expand Up @@ -176,6 +158,10 @@ class AppearancePrefsFragment : BasePrefsFragment() {
PREFERENCE_GRID_COLUMNS,
PREFERENCE_GRID_COLUMNS_DEFAULT,
)
gridColumnPref?.summary = preferenceColumns
// Show the effective (device-clamped) column count so it matches what the grid renders.
val effective =
(preferenceColumns?.toIntOrNull() ?: PREFERENCE_GRID_COLUMNS_DEFAULT.toInt())
.coerceAtMost(MainFragmentViewModel.maxGridColumns())
gridColumnPref?.summary = effective.toString()
}
}
Loading
Loading