diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryAdapter.java new file mode 100644 index 00000000..7c0cb9cb --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryAdapter.java @@ -0,0 +1,205 @@ +package com.kabouzeid.gramophone.adapter; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; + +import com.kabouzeid.appthemehelper.ThemeStore; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.util.SwipeAndDragHelper; +import com.kabouzeid.gramophone.model.Category; + +import java.util.ArrayList; + +public class CategoryAdapter extends RecyclerView.Adapter implements SwipeAndDragHelper.ActionCompletionContract { + private ArrayList categories; + private ItemTouchHelper touchHelper; + private ColorStateList color; + + public CategoryAdapter(ArrayList categories, ColorStateList color) { + this.categories = copy(categories); + this.color = color; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.preference_dialog_library_categories_listitem, parent, false); + return new CategoryViewHolder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + Category category = categories.get(position); + CategoryViewHolder h = (CategoryViewHolder) holder; + h.checkBox.setChecked(category.visible); + h.title.setText(h.title.getResources().getString(category.id.key)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + h.checkBox.setButtonTintList(color); + } else { + Drawable checkDrawable = + ContextCompat.getDrawable(h.checkBox.getContext(), R.drawable.abc_btn_check_material); + Drawable drawable = DrawableCompat.wrap(checkDrawable); + DrawableCompat.setTintList(drawable, color); + h.checkBox.setButtonDrawable(drawable); + } + + h.checkBox.setOnClickListener((view) -> { + h.checkBox.setChecked(category.visible = !category.visible); + notifyDataSetChanged(); + } + ); + + h.title.setOnClickListener((view) -> { + h.checkBox.setChecked(category.visible = !category.visible); + notifyDataSetChanged(); + } + ); + + h.dragHandle.setOnTouchListener((view, event) -> { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + touchHelper.startDrag(h); + } + return false; + } + ); + + Context context = h.dragHandle.getContext(); + int backgroundColor = ThemeStore.textColorSecondary(context); + int borderColor = ThemeStore.textColorSecondaryInverse(context); + int height = getPixel(1, h.dragHandle); + + GradientDrawable d = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{Color.RED, Color.RED}); + d.setStroke(height, backgroundColor); + d.setSize(height * 4, getPixel(16, h.dragHandle)); + + h.dragHandle.setBackground(new DragHandle(h.dragHandle.getResources().getDisplayMetrics().density, backgroundColor, borderColor)); + } + + private static class DragHandle extends Drawable { + private float density; + private Paint shape; + private Paint outline; + + public DragHandle(float density, int color, int borderColor) { + this.shape = new Paint(); + this.shape.setStyle(Paint.Style.FILL); + this.shape.setColor(color); + + this.outline = new Paint(); + this.outline.setAntiAlias(true); + this.outline.setStyle(Paint.Style.STROKE); + this.outline.setColor(borderColor); + + this.density = density; + } + + @Override + public void draw(@NonNull Canvas canvas) { + Rect bounds = getBounds(); + int left = bounds.left + 1; + int top = bounds.top + 1; + int width = bounds.width() - 1; + canvas.save(); + canvas.translate(1, 1); + canvas.drawRect(left, top, width, top + 2 * density, shape); + canvas.drawRect(left, top, width, top + 2 * density, outline); + canvas.translate(0, (density * 2) * 2); + canvas.drawRect(left, top, width, top + 2 * density, shape); + canvas.drawRect(left, top, width, top + 2 * density, outline); + canvas.restore(); + } + + @Override + public void setAlpha(int i) { + } + + @Override + public void setColorFilter(@Nullable ColorFilter filter) { + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + } + + + @Override + public int getItemCount() { + return categories.size(); + } + + @Override + public int getItemViewType(int position) { + return 1; + } + + public void setCategories(ArrayList categories) { + this.categories = copy(categories); + notifyDataSetChanged(); + } + + @Override + public void onViewMoved(int oldPosition, int newPosition) { + Category category = categories.get(oldPosition); + categories.remove(oldPosition); + categories.add(newPosition, category); + notifyItemMoved(oldPosition, newPosition); + } + + public void setTouchHelper(ItemTouchHelper touchHelper) { + this.touchHelper = touchHelper; + } + + public ArrayList getCategories() { + return categories; + } + + + private ArrayList copy(ArrayList categories) { + ArrayList result = new ArrayList<>(); + for (Category category : categories) { + result.add(new Category(category)); + } + return result; + } + + private int getPixel(float dp, View dragHandle) { + return Math.round(dp * dragHandle.getResources().getDisplayMetrics().density); + } + + private static class CategoryViewHolder extends RecyclerView.ViewHolder { + public CheckBox checkBox; + public TextView title; + public View dragHandle; + + public CategoryViewHolder(View view) { + super(view); + checkBox = view.findViewById(R.id.checkbox); + title = view.findViewById(R.id.title); + dragHandle = view.findViewById(R.id.drag_handle); + } + } +} + diff --git a/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreference.java b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreference.java new file mode 100644 index 00000000..7993d30d --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreference.java @@ -0,0 +1,24 @@ +package com.kabouzeid.gramophone.preferences; + +import android.content.Context; +import android.util.AttributeSet; + +import com.kabouzeid.appthemehelper.common.prefs.supportv7.ATEDialogPreference; + +public class LibraryPreference extends ATEDialogPreference { + public LibraryPreference(Context context) { + super(context); + } + + public LibraryPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LibraryPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public LibraryPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java new file mode 100644 index 00000000..54774614 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java @@ -0,0 +1,108 @@ +package com.kabouzeid.gramophone.preferences; + +import android.app.Dialog; +import android.content.res.ColorStateList; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.LayoutInflater; +import android.view.View; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.util.DialogUtils; +import com.kabouzeid.appthemehelper.ThemeStore; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.adapter.CategoryAdapter; +import com.kabouzeid.gramophone.model.Category; +import com.kabouzeid.gramophone.util.PreferenceUtil; +import com.kabouzeid.gramophone.util.SwipeAndDragHelper; + +import java.util.ArrayList; + + +public class LibraryPreferenceDialog extends DialogFragment { + public static LibraryPreferenceDialog newInstance() { + return new LibraryPreferenceDialog(); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.preference_dialog_library_categories, null); + + final ArrayList categories = PreferenceUtil.getInstance(getContext()).getLibraryCategories(); + RecyclerView categoriesView = view.findViewById(R.id.recycler_view); + categoriesView.setLayoutManager(new LinearLayoutManager(getActivity())); + final CategoryAdapter adapter = new CategoryAdapter(categories, getCheckboxColors()); + SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(adapter); + ItemTouchHelper touchHelper = new ItemTouchHelper(swipeAndDragHelper); + adapter.setTouchHelper(touchHelper); + categoriesView.setAdapter(adapter); + touchHelper.attachToRecyclerView(categoriesView); + + MaterialDialog dialog = new MaterialDialog.Builder(getContext()) + .title(R.string.library_categories) + .customView(view, false) + .positiveText(android.R.string.ok) + .negativeText(android.R.string.cancel) + .neutralText(R.string.reset_action) + .autoDismiss(false) + .onNeutral((dialog1, action) -> { + adapter.setCategories(PreferenceUtil.getInstance(getContext()).getDefaultLibraryCategories()); + }) + .onNegative((dialog12, action) -> dismiss()) + .onPositive((dialog13, action) -> { + if (!updateCategories(adapter.getCategories())) { + new MaterialDialog.Builder(getContext()) + .title(R.string.edit_categories) + .content(R.string.at_least_one_category_must_be_enabled) + .positiveText(android.R.string.ok) + .show(); + } else { + dismiss(); + } + }) + .build(); + + return dialog; + } + + private boolean updateCategories(ArrayList categories) { + if (getSelected(categories) == 0) return false; + + PreferenceUtil.getInstance(getContext()).setLibraryCategories(categories); + + return true; + } + + private int getSelected(ArrayList categories) { + int selected = 0; + for (Category category : categories) { + if (category.visible) + selected ++; + } + return selected; + } + + private ColorStateList getCheckboxColors() { + int disabledColor = DialogUtils.getDisabledColor(getContext()); + return new ColorStateList( + new int[][] { + new int[] {android.R.attr.state_enabled, -android.R.attr.state_checked}, + new int[] {android.R.attr.state_enabled, android.R.attr.state_checked}, + new int[] {-android.R.attr.state_enabled, -android.R.attr.state_checked}, + new int[] {-android.R.attr.state_enabled, android.R.attr.state_checked} + }, + new int[] { + DialogUtils.resolveColor(getContext(), R.attr.colorControlNormal), + ThemeStore.accentColor(getContext()), + disabledColor, + disabledColor + }); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java index f593c912..8e2d9e6e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java @@ -31,6 +31,8 @@ import com.kabouzeid.gramophone.appshortcuts.DynamicShortcutManager; import com.kabouzeid.gramophone.misc.NonProAllowedColors; import com.kabouzeid.gramophone.preferences.BlacklistPreference; import com.kabouzeid.gramophone.preferences.BlacklistPreferenceDialog; +import com.kabouzeid.gramophone.preferences.LibraryPreference; +import com.kabouzeid.gramophone.preferences.LibraryPreferenceDialog; import com.kabouzeid.gramophone.preferences.NowPlayingScreenPreference; import com.kabouzeid.gramophone.preferences.NowPlayingScreenPreferenceDialog; import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity; @@ -167,6 +169,8 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia return NowPlayingScreenPreferenceDialog.newInstance(); } else if (preference instanceof BlacklistPreference) { return BlacklistPreferenceDialog.newInstance(); + } else if (preference instanceof LibraryPreference) { + return LibraryPreferenceDialog.newInstance(); } return super.onCreatePreferenceDialog(preference); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java b/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java new file mode 100644 index 00000000..3e28263d --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java @@ -0,0 +1,56 @@ +package com.kabouzeid.gramophone.util; + +import android.graphics.Canvas; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; + +public class SwipeAndDragHelper extends ItemTouchHelper.Callback { + + private ActionCompletionContract contract; + + public SwipeAndDragHelper(ActionCompletionContract contract) { + this.contract = contract; + } + + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; + return makeMovementFlags(dragFlags, 0); + } + + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + contract.onViewMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition()); + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + } + + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + @Override + public void onChildDraw(Canvas c, + RecyclerView recyclerView, + RecyclerView.ViewHolder viewHolder, + float dX, + float dY, + int actionState, + boolean isCurrentlyActive) { + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth()); + viewHolder.itemView.setAlpha(alpha); + } + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + + public interface ActionCompletionContract { + void onViewMoved(int oldPosition, int newPosition); + } + +} + diff --git a/app/src/main/res/layout/preference_dialog_library_categories.xml b/app/src/main/res/layout/preference_dialog_library_categories.xml new file mode 100644 index 00000000..4782ec71 --- /dev/null +++ b/app/src/main/res/layout/preference_dialog_library_categories.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/layout/preference_dialog_library_categories_listitem.xml b/app/src/main/res/layout/preference_dialog_library_categories_listitem.xml new file mode 100644 index 00000000..3446e8b8 --- /dev/null +++ b/app/src/main/res/layout/preference_dialog_library_categories_listitem.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1befbdde..0c8fbb68 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -140,6 +140,7 @@ Entfernen Umbenennen Erstellen + Zurücksetzen %1$d ausgewählt Hauptfarbe Akzentfarbe @@ -284,4 +285,8 @@ Der Inhalt der Ordner auf der Blacklist wird in deiner Bibliothek verborgen. Künstler-Bild zurücksetzen Künstler-Bild festlegen + Bibliothek + Anzeige und Anordnung der einzelnen Kategorien festlegen. + Kategorien bearbeiten + Zumindest eine Kategorie muss aktiv sein. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0a05e391..0fd77e6f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -141,6 +141,7 @@ Remove Rename Create + Reset %1$d selected Primary color Accent color @@ -294,4 +295,8 @@ The content of blacklisted folders is hidden from your library. Reset artist image Set artist image + Library categories + Configure visibility and order of library categories. + Edit categories + At least one category must be enabled. diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index ed43ede6..56816287 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -20,6 +20,11 @@ android:positiveButtonText="@null" android:title="@string/pref_title_general_theme" /> + +