diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ColorChooserDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ColorChooserDialog.java index 09d7e542..07be46dd 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ColorChooserDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ColorChooserDialog.java @@ -2,157 +2,212 @@ package com.kabouzeid.gramophone.dialogs; import android.app.Activity; import android.app.Dialog; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.RippleDrawable; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.StateListDrawable; -import android.graphics.drawable.shapes.OvalShape; -import android.os.Build; import android.os.Bundle; +import android.support.annotation.ColorInt; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.annotation.StringRes; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.GridView; +import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; -import com.kabouzeid.gramophone.util.ColorUtil; -import com.kabouzeid.gramophone.views.ColorView; +import com.kabouzeid.gramophone.views.SelectableColorView; /** - * @author Aidan Follestad (afollestad) + * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) */ public class ColorChooserDialog extends LeakDetectDialogFragment implements View.OnClickListener { - private ColorCallback mCallback; - private int[] mColors; - private GridView mGrid; + private Colors colors; public ColorChooserDialog() { } - private static int translucentColor(int color) { - final float factor = 0.7f; - int alpha = Math.round(Color.alpha(color) * factor); - int red = Color.red(color); - int green = Color.green(color); - int blue = Color.blue(color); - return Color.argb(alpha, red, green, blue); - } - - @NonNull - private static Drawable createSelector(int color) { - ShapeDrawable darkerCircle = new ShapeDrawable(new OvalShape()); - darkerCircle.getPaint().setColor(translucentColor(ColorUtil.shiftColorDown(color))); - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, darkerCircle); - return stateListDrawable; - } + private int mCircleSize; + private ColorCallback mCallback; + private GridView mGrid; @Override public void onAttach(Activity activity) { super.onAttach(activity); + if (!(activity instanceof ColorCallback)) + throw new IllegalStateException("ColorChooserDialog needs to be shown from an Activity implementing ColorCallback."); mCallback = (ColorCallback) activity; } + private boolean isInSub() { + return getArguments().getBoolean("in_sub", false); + } + + private void setInSub(boolean value) { + getArguments().putBoolean("in_sub", value); + if (value) { + ((MaterialDialog) getDialog()).setActionButton(DialogAction.NEUTRAL, R.string.back); + } else { + ((MaterialDialog) getDialog()).setActionButton(DialogAction.NEUTRAL, null); + } + } + + private int getTopIndex() { + return getArguments().getInt("top_index", -1); + } + + private void setTopIndex(int value) { + if (getTopIndex() != value) + setSubIndex(colors.headerColorIndexes[value]); + getArguments().putInt("top_index", value); + } + + private int getSubIndex() { + return getArguments().getInt("sub_index", -1); + } + + private void setSubIndex(int value) { + getArguments().putInt("sub_index", value); + } + + private int getPreselectColor() { + return getArguments().getInt("color_preselect", -1); + } + @Override - public void onClick(@NonNull View v) { + public void onClick(View v) { if (v.getTag() != null) { final int index = (Integer) v.getTag(); - getArguments().putInt("preselect", mColors[index]); + if (isInSub()) { + setSubIndex(index); + } else { + setTopIndex(index); + setInSub(true); + } invalidateGrid(); } } + public interface ColorCallback { + void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor); + } + + private void setColors() { + colors = Colors.fromBundle(getArguments()); + } + + private void setIndexesFor(@ColorInt int color) { + if (getTopIndex() != -1) return; + if (color != -1) { + for (int i = 0; i < colors.colors.length; i++) { + for (int z = 0; z < colors.colors[i].length; z++) { + if (color == colors.colors[i][z]) { + setTopIndex(i); + setSubIndex(z); + return; + } + } + } + } + } + + public int getTitleRes() { + return getArguments().getInt("title", 0); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + setColors(); + setIndexesFor(getPreselectColor()); + + final DisplayMetrics dm = getResources().getDisplayMetrics(); + mCircleSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 56, dm); + mGrid = new GridView(getContext()); + mGrid.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + mGrid.setColumnWidth(mCircleSize); + mGrid.setNumColumns(GridView.AUTO_FIT); + + final int eightDp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, dm); + mGrid.setVerticalSpacing(eightDp); + mGrid.setHorizontalSpacing(eightDp); + + final int sixteenDp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, dm); + mGrid.setPadding(sixteenDp, sixteenDp, sixteenDp, sixteenDp); + mGrid.setClipToPadding(false); + mGrid.setStretchMode(GridView.STRETCH_COLUMN_WIDTH); + mGrid.setGravity(Gravity.CENTER); + MaterialDialog dialog = new MaterialDialog.Builder(getActivity()) - .title(getArguments().getInt("title", 0)) + .title(getTitleRes()) .autoDismiss(false) - .customView(R.layout.dialog_color_chooser, false) - .neutralText(R.string.default_str) + .customView(mGrid, false) .positiveText(R.string.select) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { super.onPositive(dialog); - final int title = getArguments().getInt("title", 0); - final int preselect = getArguments().getInt("preselect", -1); - mCallback.onColorSelection(title, preselect); + mCallback.onColorSelection(ColorChooserDialog.this, getSelectedColor()); dismiss(); } @Override public void onNeutral(MaterialDialog dialog) { super.onNeutral(dialog); - if (getArguments().getInt("title", 0) == R.string.primary_color) { - getArguments().putInt("preselect", ContextCompat.getColor(getContext(), R.color.indigo_500)); - } else if (getArguments().getInt("title", 0) == R.string.accent_color) { - getArguments().putInt("preselect", ContextCompat.getColor(getContext(), R.color.pink_A200)); - } + setInSub(false); invalidateGrid(); } - }) - .build(); - - final boolean primary = getArguments().getInt("title", 0) == R.string.primary_color; - final TypedArray ta = getActivity().getResources().obtainTypedArray( - primary ? R.array.colors_primary : R.array.colors_accent); - mColors = new int[ta.length()]; - for (int i = 0; i < ta.length(); i++) - mColors[i] = ta.getColor(i, 0); - ta.recycle(); - mGrid = (GridView) dialog.getCustomView(); - if (mGrid != null) { - mGrid.setNumColumns(primary ? 7 : 4); - invalidateGrid(); + }).build(); + invalidateGrid(); + if (isInSub()) { + dialog.setActionButton(DialogAction.NEUTRAL, R.string.back); } return dialog; } + @ColorInt + private int getSelectedColor() { + int selectedColor = 0; + int topIndex = getTopIndex(); + int subIndex = getSubIndex(); + if (topIndex != -1 && subIndex != -1) { + selectedColor = colors.colors[topIndex][subIndex]; + } + return selectedColor; + } + private void invalidateGrid() { if (mGrid.getAdapter() == null) { mGrid.setAdapter(new ColorGridAdapter()); - mGrid.setSelector(ResourcesCompat.getDrawable(getResources(), R.drawable.md_transparent, null)); - } else ((BaseAdapter) mGrid.getAdapter()).notifyDataSetChanged(); + } else { + ((BaseAdapter) mGrid.getAdapter()).notifyDataSetChanged(); + } } - public void show(@NonNull AppCompatActivity activity, @StringRes int title, int preselect) { - Bundle args = new Bundle(); - args.putInt("preselect", preselect); - args.putInt("title", title); - setArguments(args); - show(activity.getSupportFragmentManager(), "COLOR_SELECTOR"); - } - - public interface ColorCallback { - void onColorSelection(int title, int color); - } - - private class ColorGridAdapter extends BaseAdapter implements View.OnClickListener { + private class ColorGridAdapter extends BaseAdapter { public ColorGridAdapter() { } @Override public int getCount() { - return mColors.length; + if (isInSub()) { + return colors.colors[getTopIndex()].length; + } else { + return colors.colors.length; + } } @Override public Object getItem(int position) { - return mColors[position]; + if (isInSub()) { + return colors.colors[getTopIndex()][position]; + } else { + return colors.colors[position][colors.headerColorIndexes[position]]; + } } @Override @@ -160,39 +215,65 @@ public class ColorChooserDialog extends LeakDetectDialogFragment implements View return position; } - @Nullable @Override - public View getView(int position, @Nullable View convertView, ViewGroup parent) { - if (convertView == null) - convertView = LayoutInflater.from(getActivity()).inflate(R.layout.griditem_color_chooser, parent, false); - - final ColorView colorView = (ColorView) convertView; - colorView.setActivated(getArguments().getInt("preselect") == mColors[position]); - colorView.setBackgroundColor(mColors[position]); - colorView.setTag(position); - colorView.setOnClickListener(this); - - Drawable selector = createSelector(mColors[position]); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - int[][] states = new int[][]{ - new int[]{android.R.attr.state_pressed} - }; - int[] colors = new int[]{ - ColorUtil.shiftColorDown(mColors[position]) - }; - ColorStateList rippleColors = new ColorStateList(states, colors); - colorView.setForeground(new RippleDrawable(rippleColors, selector, null)); - } else { - colorView.setForeground(selector); + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = new SelectableColorView(getContext()); + convertView.setLayoutParams(new GridView.LayoutParams(mCircleSize, mCircleSize)); } + SelectableColorView child = (SelectableColorView) convertView; + if (isInSub()) { + child.setBackgroundColor(colors.colors[getTopIndex()][position]); + child.setSelected(getSubIndex() == position); + } else { + child.setBackgroundColor(colors.colors[position][colors.headerColorIndexes[position]]); + child.setSelected(getTopIndex() == position); + } + child.setTag(position); + child.setOnClickListener(ColorChooserDialog.this); return convertView; } + } - @Override - public void onClick(@NonNull View v) { - final int index = (Integer) v.getTag(); - getArguments().putInt("preselect", mColors[index]); - invalidateGrid(); + public static ColorChooserDialog create(@StringRes int title, @NonNull Colors colors, @ColorInt int preselectColor) { + ColorChooserDialog dialog = new ColorChooserDialog(); + Bundle args = new Bundle(); + args.putInt("title", title); + args.putInt("color_preselect", preselectColor); + + Colors.toBundle(colors, args); + + dialog.setArguments(args); + return dialog; + } + + public static final class Colors { + final int[] headerColorIndexes; + final int[][] colors; + + public Colors(int[] headerColorIndexes, int[][] colors) { + if (headerColorIndexes.length != colors.length) { + throw new IllegalArgumentException("int[] headerColorIndexes and int[][] colors must have the same length"); + } + this.headerColorIndexes = headerColorIndexes; + this.colors = colors; + } + + static void toBundle(Colors colors, Bundle bundle) { + bundle.putIntArray("top_colors", colors.headerColorIndexes); + for (int i = 0; i < colors.colors.length; i++) { + bundle.putIntArray("sub_colors_" + i, colors.colors[i]); + } + } + + static Colors fromBundle(Bundle bundle) { + int[] headerColorIndexes = bundle.getIntArray("top_colors"); + if (headerColorIndexes == null) return new Colors(new int[]{}, new int[][]{}); + int[][] colors = new int[headerColorIndexes.length][]; + for (int i = 0; i < colors.length; i++) { + colors[i] = bundle.getIntArray("sub_colors_" + i); + } + return new Colors(headerColorIndexes, colors); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MaterialColorHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MaterialColorHelper.java new file mode 100644 index 00000000..371fb4a0 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MaterialColorHelper.java @@ -0,0 +1,388 @@ +package com.kabouzeid.gramophone.helper; + +import android.graphics.Color; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public class MaterialColorHelper { + public static int[] createPrimaryColorIndexes() { + return new int[]{ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + }; + } + + public static int[][] createPrimaryColors() { + return new int[][]{ + new int[]{ + Color.parseColor("#FFEBEE"), + Color.parseColor("#FFCDD2"), + Color.parseColor("#EF9A9A"), + Color.parseColor("#E57373"), + Color.parseColor("#EF5350"), + Color.parseColor("#F44336"), + Color.parseColor("#E53935"), + Color.parseColor("#D32F2F"), + Color.parseColor("#C62828"), + Color.parseColor("#B71C1C") + }, + new int[]{ + Color.parseColor("#FCE4EC"), + Color.parseColor("#F8BBD0"), + Color.parseColor("#F48FB1"), + Color.parseColor("#F06292"), + Color.parseColor("#EC407A"), + Color.parseColor("#E91E63"), + Color.parseColor("#D81B60"), + Color.parseColor("#C2185B"), + Color.parseColor("#AD1457"), + Color.parseColor("#880E4F") + }, + new int[]{ + Color.parseColor("#F3E5F5"), + Color.parseColor("#E1BEE7"), + Color.parseColor("#CE93D8"), + Color.parseColor("#BA68C8"), + Color.parseColor("#AB47BC"), + Color.parseColor("#9C27B0"), + Color.parseColor("#8E24AA"), + Color.parseColor("#7B1FA2"), + Color.parseColor("#6A1B9A"), + Color.parseColor("#4A148C") + }, + new int[]{ + Color.parseColor("#EDE7F6"), + Color.parseColor("#D1C4E9"), + Color.parseColor("#B39DDB"), + Color.parseColor("#9575CD"), + Color.parseColor("#7E57C2"), + Color.parseColor("#673AB7"), + Color.parseColor("#5E35B1"), + Color.parseColor("#512DA8"), + Color.parseColor("#4527A0"), + Color.parseColor("#311B92") + }, + new int[]{ + Color.parseColor("#E8EAF6"), + Color.parseColor("#C5CAE9"), + Color.parseColor("#9FA8DA"), + Color.parseColor("#7986CB"), + Color.parseColor("#5C6BC0"), + Color.parseColor("#3F51B5"), + Color.parseColor("#3949AB"), + Color.parseColor("#303F9F"), + Color.parseColor("#283593"), + Color.parseColor("#1A237E") + }, + new int[]{ + Color.parseColor("#E3F2FD"), + Color.parseColor("#BBDEFB"), + Color.parseColor("#90CAF9"), + Color.parseColor("#64B5F6"), + Color.parseColor("#42A5F5"), + Color.parseColor("#2196F3"), + Color.parseColor("#1E88E5"), + Color.parseColor("#1976D2"), + Color.parseColor("#1565C0"), + Color.parseColor("#0D47A1") + }, + new int[]{ + Color.parseColor("#E1F5FE"), + Color.parseColor("#B3E5FC"), + Color.parseColor("#81D4FA"), + Color.parseColor("#4FC3F7"), + Color.parseColor("#29B6F6"), + Color.parseColor("#03A9F4"), + Color.parseColor("#039BE5"), + Color.parseColor("#0288D1"), + Color.parseColor("#0277BD"), + Color.parseColor("#01579B") + }, + new int[]{ + Color.parseColor("#E0F7FA"), + Color.parseColor("#B2EBF2"), + Color.parseColor("#80DEEA"), + Color.parseColor("#4DD0E1"), + Color.parseColor("#26C6DA"), + Color.parseColor("#00BCD4"), + Color.parseColor("#00ACC1"), + Color.parseColor("#0097A7"), + Color.parseColor("#00838F"), + Color.parseColor("#006064") + }, + new int[]{ + Color.parseColor("#E0F2F1"), + Color.parseColor("#B2DFDB"), + Color.parseColor("#80CBC4"), + Color.parseColor("#4DB6AC"), + Color.parseColor("#26A69A"), + Color.parseColor("#009688"), + Color.parseColor("#00897B"), + Color.parseColor("#00796B"), + Color.parseColor("#00695C"), + Color.parseColor("#004D40") + }, + new int[]{ + Color.parseColor("#E8F5E9"), + Color.parseColor("#C8E6C9"), + Color.parseColor("#A5D6A7"), + Color.parseColor("#81C784"), + Color.parseColor("#66BB6A"), + Color.parseColor("#4CAF50"), + Color.parseColor("#43A047"), + Color.parseColor("#388E3C"), + Color.parseColor("#2E7D32"), + Color.parseColor("#1B5E20") + }, + new int[]{ + Color.parseColor("#F1F8E9"), + Color.parseColor("#DCEDC8"), + Color.parseColor("#C5E1A5"), + Color.parseColor("#AED581"), + Color.parseColor("#9CCC65"), + Color.parseColor("#8BC34A"), + Color.parseColor("#7CB342"), + Color.parseColor("#689F38"), + Color.parseColor("#558B2F"), + Color.parseColor("#33691E") + }, + new int[]{ + Color.parseColor("#F9FBE7"), + Color.parseColor("#F0F4C3"), + Color.parseColor("#E6EE9C"), + Color.parseColor("#DCE775"), + Color.parseColor("#D4E157"), + Color.parseColor("#CDDC39"), + Color.parseColor("#C0CA33"), + Color.parseColor("#AFB42B"), + Color.parseColor("#9E9D24"), + Color.parseColor("#827717") + }, + new int[]{ + Color.parseColor("#FFFDE7"), + Color.parseColor("#FFF9C4"), + Color.parseColor("#FFF59D"), + Color.parseColor("#FFF176"), + Color.parseColor("#FFEE58"), + Color.parseColor("#FFEB3B"), + Color.parseColor("#FDD835"), + Color.parseColor("#FBC02D"), + Color.parseColor("#F9A825"), + Color.parseColor("#F57F17") + }, + new int[]{ + Color.parseColor("#FFF8E1"), + Color.parseColor("#FFECB3"), + Color.parseColor("#FFE082"), + Color.parseColor("#FFD54F"), + Color.parseColor("#FFCA28"), + Color.parseColor("#FFC107"), + Color.parseColor("#FFB300"), + Color.parseColor("#FFA000"), + Color.parseColor("#FF8F00"), + Color.parseColor("#FF6F00") + }, + new int[]{ + Color.parseColor("#FFF3E0"), + Color.parseColor("#FFE0B2"), + Color.parseColor("#FFCC80"), + Color.parseColor("#FFB74D"), + Color.parseColor("#FFA726"), + Color.parseColor("#FF9800"), + Color.parseColor("#FB8C00"), + Color.parseColor("#F57C00"), + Color.parseColor("#EF6C00"), + Color.parseColor("#E65100") + }, + new int[]{ + Color.parseColor("#FBE9E7"), + Color.parseColor("#FFCCBC"), + Color.parseColor("#FFAB91"), + Color.parseColor("#FF8A65"), + Color.parseColor("#FF7043"), + Color.parseColor("#FF5722"), + Color.parseColor("#F4511E"), + Color.parseColor("#E64A19"), + Color.parseColor("#D84315"), + Color.parseColor("#BF360C") + }, + new int[]{ + Color.parseColor("#EFEBE9"), + Color.parseColor("#D7CCC8"), + Color.parseColor("#BCAAA4"), + Color.parseColor("#A1887F"), + Color.parseColor("#8D6E63"), + Color.parseColor("#795548"), + Color.parseColor("#6D4C41"), + Color.parseColor("#5D4037"), + Color.parseColor("#4E342E"), + Color.parseColor("#3E2723") + }, + new int[]{ + Color.parseColor("#FAFAFA"), + Color.parseColor("#F5F5F5"), + Color.parseColor("#EEEEEE"), + Color.parseColor("#E0E0E0"), + Color.parseColor("#BDBDBD"), + Color.parseColor("#9E9E9E"), + Color.parseColor("#757575"), + Color.parseColor("#616161"), + Color.parseColor("#424242"), + Color.parseColor("#212121"), + }, + new int[]{ + Color.parseColor("#ECEFF1"), + Color.parseColor("#CFD8DC"), + Color.parseColor("#B0BEC5"), + Color.parseColor("#90A4AE"), + Color.parseColor("#78909C"), + Color.parseColor("#607D8B"), + Color.parseColor("#546E7A"), + Color.parseColor("#455A64"), + Color.parseColor("#37474F"), + Color.parseColor("#263238") + } + }; + } + + public static int[] createAccentColorIndexes() { + return new int[]{ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + }; + } + + public static int[][] createAccentColors() { + return new int[][]{ + new int[]{ + Color.parseColor("#ff8a80"), + Color.parseColor("#ff5252"), + Color.parseColor("#ff1744"), + Color.parseColor("#d50000") + }, + + new int[]{ + Color.parseColor("#ff80ab"), + Color.parseColor("#ff4081"), + Color.parseColor("#f50057"), + Color.parseColor("#c51162") + }, + new int[]{ + Color.parseColor("#ea80fc"), + Color.parseColor("#e040fb"), + Color.parseColor("#d500f9"), + Color.parseColor("#aa00ff") + }, + new int[]{ + Color.parseColor("#b388ff"), + Color.parseColor("#7c4dff"), + Color.parseColor("#651fff"), + Color.parseColor("#6200ea") + }, + new int[]{ + Color.parseColor("#8c9eff"), + Color.parseColor("#536dfe"), + Color.parseColor("#3d5afe"), + Color.parseColor("#304ffe") + }, + new int[]{ + Color.parseColor("#82b1ff"), + Color.parseColor("#448aff"), + Color.parseColor("#2979ff"), + Color.parseColor("#2962ff") + }, + new int[]{ + Color.parseColor("#80d8ff"), + Color.parseColor("#40c4ff"), + Color.parseColor("#00b0ff"), + Color.parseColor("#0091ea") + }, + new int[]{ + Color.parseColor("#84ffff"), + Color.parseColor("#18ffff"), + Color.parseColor("#00e5ff"), + Color.parseColor("#00b8d4") + }, + new int[]{ + Color.parseColor("#a7ffeb"), + Color.parseColor("#64ffda"), + Color.parseColor("#1de9b6"), + Color.parseColor("#00bfa5") + }, + new int[]{ + Color.parseColor("#b9f6ca"), + Color.parseColor("#69f0ae"), + Color.parseColor("#00e676"), + Color.parseColor("#00c853") + }, + new int[]{ + Color.parseColor("#ccff90"), + Color.parseColor("#b2ff59"), + Color.parseColor("#76ff03"), + Color.parseColor("#64dd17") + }, + new int[]{ + Color.parseColor("#f4ff81"), + Color.parseColor("#eeff41"), + Color.parseColor("#c6ff00"), + Color.parseColor("#aeea00") + }, + new int[]{ + Color.parseColor("#ffff8d"), + Color.parseColor("#ffff00"), + Color.parseColor("#ffea00"), + Color.parseColor("#ffd600") + }, + new int[]{ + Color.parseColor("#ffe57f"), + Color.parseColor("#ffd740"), + Color.parseColor("#ffc400"), + Color.parseColor("#ffab00") + }, + new int[]{ + Color.parseColor("#ffd180"), + Color.parseColor("#ffab40"), + Color.parseColor("#ff9100"), + Color.parseColor("#ff6d00") + }, + new int[]{ + Color.parseColor("#ff9e80"), + Color.parseColor("#ff6e40"), + Color.parseColor("#ff3d00"), + Color.parseColor("#dd2c00") + }, + }; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/prefs/DynamicPreferenceCategory.java b/app/src/main/java/com/kabouzeid/gramophone/prefs/DynamicPreferenceCategory.java index 21553c80..dc0c8a02 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/prefs/DynamicPreferenceCategory.java +++ b/app/src/main/java/com/kabouzeid/gramophone/prefs/DynamicPreferenceCategory.java @@ -25,7 +25,7 @@ public class DynamicPreferenceCategory extends PreferenceCategory { this(context, null, 0); } - public DynamicPreferenceCategory(@NonNull Context context, @NonNull AttributeSet attrs, int defStyleAttr) { + public DynamicPreferenceCategory(@NonNull Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setLayoutResource(R.layout.preference_category_custom); setSelectable(false); 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 3eeee80b..68db6834 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 @@ -10,6 +10,7 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; +import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.Toolbar; @@ -18,6 +19,7 @@ import android.view.View; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.ColorChooserDialog; +import com.kabouzeid.gramophone.helper.MaterialColorHelper; import com.kabouzeid.gramophone.prefs.ColorChooserPreference; import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity; import com.kabouzeid.gramophone.util.NavigationUtil; @@ -48,11 +50,14 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia } @Override - public void onColorSelection(int title, int color) { - if (title == R.string.primary_color) { - PreferenceUtil.getInstance(this).setThemeColorPrimary(color); - } else if (title == R.string.accent_color) { - PreferenceUtil.getInstance(this).setThemeColorAccent(color); + public void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor) { + switch (dialog.getTitleRes()) { + case R.string.primary_color: + PreferenceUtil.getInstance(this).setThemeColorPrimary(selectedColor); + break; + case R.string.accent_color: + PreferenceUtil.getInstance(this).setThemeColorAccent(selectedColor); + break; } recreateIfThemeChanged(); } @@ -133,10 +138,12 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia primaryColor.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(@NonNull Preference preference) { - new ColorChooserDialog().show( - ((SettingsActivity) getActivity()), - preference.getTitleRes(), - ((SettingsActivity) getActivity()).getThemeColorPrimary()); + ColorChooserDialog + .create(preference.getTitleRes(), + new ColorChooserDialog.Colors(MaterialColorHelper.createPrimaryColorIndexes(), MaterialColorHelper.createPrimaryColors()), + PreferenceUtil.getInstance(getActivity()).getThemeColorPrimary(getActivity()) + ) + .show(((SettingsActivity) getActivity()).getSupportFragmentManager(), "COLOR_CHOOSER"); return true; } }); @@ -146,10 +153,12 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia accentColor.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(@NonNull Preference preference) { - new ColorChooserDialog().show( - ((SettingsActivity) getActivity()), - preference.getTitleRes(), - ((SettingsActivity) getActivity()).getThemeColorAccent()); + ColorChooserDialog + .create(preference.getTitleRes(), + new ColorChooserDialog.Colors(MaterialColorHelper.createAccentColorIndexes(), MaterialColorHelper.createAccentColors()), + PreferenceUtil.getInstance(getActivity()).getThemeColorAccent(getActivity()) + ) + .show(((SettingsActivity) getActivity()).getSupportFragmentManager(), "COLOR_CHOOSER"); return true; } }); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java index bb21630f..68491b10 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java @@ -80,7 +80,7 @@ import butterknife.ButterKnife; /** * @author Karim Abou Zeid (kabouzeid) - *
+ *
* Do not use {@link #setContentView(int)} but wrap your layout with
* {@link #wrapSlidingMusicPanelAndFab(int)} first and then return it in {@link #createContentView()}
*/
@@ -669,24 +669,14 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi
setSeekBarTint(progressSlider, thumbColor, progressColor);
}
- // note that this is not exactly the same as in ColorUtil
- @SuppressWarnings("ResourceType")
+ @ColorInt
private static int shiftColorUp(@ColorInt int color) {
- int alpha = Color.alpha(color);
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] *= 1.2f; // value component
- return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv));
+ return ColorUtil.shiftColor(color, 1.2f);
}
- // note that this is not exactly the same as in ColorUtil
- @SuppressWarnings("ResourceType")
+ @ColorInt
private static int shiftColorDown(@ColorInt int color) {
- int alpha = Color.alpha(color);
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] *= 0.8f; // value component
- return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv));
+ return ColorUtil.shiftColor(color, 0.8f);
}
private static void setSeekBarTint(SeekBar seekBar, @ColorInt int thumbColor, @ColorInt int progressColor) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java
index 61feaf5c..93b932e3 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java
@@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
@@ -19,6 +20,7 @@ import com.kabouzeid.gramophone.R;
public class ColorUtil {
public static final int PALETTE_BITMAP_SIZE = 100;
+ @ColorInt
public static int generateColor(Context context, Bitmap bitmap) {
return getColor(context, generatePalette(bitmap));
}
@@ -29,6 +31,7 @@ public class ColorUtil {
.generate();
}
+ @ColorInt
public static int getColor(Context context, @Nullable Palette palette) {
if (palette != null) {
if (palette.getVibrantSwatch() != null) {
@@ -48,6 +51,7 @@ public class ColorUtil {
return ColorUtil.resolveColor(context, R.attr.default_bar_color);
}
+ @ColorInt
public static int resolveColor(@NonNull Context context, @AttrRes int colorAttr) {
TypedArray a = context.obtainStyledAttributes(new int[]{colorAttr});
int resId = a.getColor(0, 0);
@@ -55,38 +59,46 @@ public class ColorUtil {
return resId;
}
+ @ColorInt
public static int getOpaqueColor(@ColorInt int color) {
return color | 0xFF000000;
}
+ @ColorInt
public static int getColorWithAlpha(float alpha, @ColorInt int baseColor) {
int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;
int rgb = 0x00ffffff & baseColor;
return a + rgb;
}
+ @ColorInt
+ public static int shiftColor(@ColorInt int color, @FloatRange(from = 0.0f, to = 2.0f) float by) {
+ if (by == 1f) return color;
+ int alpha = Color.alpha(color);
+ float[] hsv = new float[3];
+ Color.colorToHSV(color, hsv);
+ hsv[2] *= by; // value component
+ return getColorWithAlpha(alpha, Color.HSVToColor(hsv));
+ }
+
@SuppressWarnings("ResourceType")
+ @ColorInt
public static int shiftColorDown(@ColorInt int color) {
- int alpha = Color.alpha(color);
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] *= 0.9f; // value component
- return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv));
+ return shiftColor(color, 0.9f);
}
@SuppressWarnings("ResourceType")
+ @ColorInt
public static int shiftColorUp(@ColorInt int color) {
- int alpha = Color.alpha(color);
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] *= 1.1f; // value component
- return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv));
+ return shiftColor(color, 1.1f);
}
+ @ColorInt
public static int getPrimaryTextColor(final Context context, boolean dark) {
return dark ? ContextCompat.getColor(context, R.color.primary_text_default_material_light) : ContextCompat.getColor(context, R.color.primary_text_default_material_dark);
}
+ @ColorInt
public static int getSecondaryTextColor(final Context context, boolean dark) {
return dark ? ContextCompat.getColor(context, R.color.secondary_text_default_material_light) : ContextCompat.getColor(context, R.color.secondary_text_default_material_dark);
}
@@ -103,14 +115,17 @@ public class ColorUtil {
return getLuminance(backgroundColor) > (255f / 1.3f);
}
+ @ColorInt
public static int getPrimaryTextColorForBackground(final Context context, @ColorInt int backgroundColor) {
return getPrimaryTextColor(context, useDarkTextColorOnBackground(backgroundColor));
}
+ @ColorInt
public static int getSecondaryTextColorForBackground(final Context context, @ColorInt int backgroundColor) {
return getSecondaryTextColor(context, useDarkTextColorOnBackground(backgroundColor));
}
+ @ColorInt
public static int getFabDrawableColorForBackground(final Context context, @ColorInt int backgroundColor) {
return getPrimaryTextColor(context, useDarkFabDrawableOnBackground(backgroundColor));
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/Util.java b/app/src/main/java/com/kabouzeid/gramophone/util/Util.java
index 905bcaae..abbd99b3 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/Util.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/Util.java
@@ -9,6 +9,7 @@ import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.AttrRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -80,6 +81,13 @@ public class Util {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}
+ public static Drawable resolveDrawable(@NonNull Context context, @AttrRes int drawableAttr) {
+ TypedArray a = context.obtainStyledAttributes(new int[]{drawableAttr});
+ Drawable drawable = a.getDrawable(0);
+ a.recycle();
+ return drawable;
+ }
+
public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableResId, int color) {
Drawable drawable = ContextCompat.getDrawable(context, drawableResId);
if (drawable != null) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/ColorView.java b/app/src/main/java/com/kabouzeid/gramophone/views/ColorView.java
index e4f25878..a79ee585 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/views/ColorView.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/views/ColorView.java
@@ -1,13 +1,8 @@
package com.kabouzeid.gramophone.views;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.widget.FrameLayout;
@@ -17,11 +12,9 @@ import com.kabouzeid.gramophone.util.ColorUtil;
public class ColorView extends FrameLayout {
- private final Bitmap mCheck;
private final Paint paint;
private final Paint paintBorder;
private final int borderWidth;
- private Paint paintCheck;
public ColorView(@NonNull Context context) {
this(context, null, 0);
@@ -33,10 +26,7 @@ public class ColorView extends FrameLayout {
public ColorView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- final int checkSize = (int) context.getResources().getDimension(R.dimen.circle_view_check);
- mCheck = getResizedBitmap(BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_checkbox_marked_circle_outline_white_24dp), checkSize, checkSize);
- borderWidth = (int) getResources().getDimension(R.dimen.circle_view_border);
+ borderWidth = getResources().getDimensionPixelSize(R.dimen.color_view_border);
paint = new Paint();
paint.setAntiAlias(true);
@@ -44,31 +34,13 @@ public class ColorView extends FrameLayout {
paintBorder = new Paint();
paintBorder.setAntiAlias(true);
- paintCheck = new Paint();
- paintCheck.setAntiAlias(true);
-
setWillNotDraw(false);
}
- private static Bitmap getResizedBitmap(@NonNull Bitmap bm, int newHeight, int newWidth) {
- int width = bm.getWidth();
- int height = bm.getHeight();
- float scaleWidth = ((float) newWidth) / width;
- float scaleHeight = ((float) newHeight) / height;
- Matrix matrix = new Matrix();
- matrix.postScale(scaleWidth, scaleHeight);
- Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
- if (bm != resizedBitmap) {
- bm.recycle();
- }
- return resizedBitmap;
- }
-
@Override
public void setBackgroundColor(int color) {
paint.setColor(color);
paintBorder.setColor(ColorUtil.shiftColorDown(color));
- paintCheck.setColorFilter(new PorterDuffColorFilter(ColorUtil.getFabDrawableColorForBackground(getContext(), color), PorterDuff.Mode.SRC_IN));
requestLayout();
invalidate();
}
@@ -102,10 +74,5 @@ public class ColorView extends FrameLayout {
int circleCenter = (canvasSize - (borderWidth * 2)) / 2;
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, ((canvasSize - (borderWidth * 2)) / 2) + borderWidth - 4.0f, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, ((canvasSize - (borderWidth * 2)) / 2) - 4.0f, paint);
-
- if (isActivated()) {
- final int offset = (canvasSize / 2) - (mCheck.getWidth() / 2);
- canvas.drawBitmap(mCheck, offset, offset, paintCheck);
- }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/SelectableColorView.java b/app/src/main/java/com/kabouzeid/gramophone/views/SelectableColorView.java
new file mode 100644
index 00000000..ea868c70
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/views/SelectableColorView.java
@@ -0,0 +1,184 @@
+package com.kabouzeid.gramophone.views;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.FrameLayout;
+
+import com.kabouzeid.gramophone.R;
+import com.kabouzeid.gramophone.util.ColorUtil;
+
+
+public class SelectableColorView extends FrameLayout {
+
+ private final int borderWidthSmall;
+ private final int borderWidthLarge;
+
+ private Paint outerPaint;
+ private Paint gapPaint;
+ private Paint innerPaint;
+ private boolean mSelected;
+
+ public SelectableColorView(Context context) {
+ this(context, null, 0);
+ }
+
+ public SelectableColorView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SelectableColorView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ final Resources r = getResources();
+ borderWidthSmall = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, r.getDisplayMetrics());
+ borderWidthLarge = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, r.getDisplayMetrics());
+
+ gapPaint = new Paint();
+ gapPaint.setAntiAlias(true);
+ gapPaint.setColor(ColorUtil.resolveColor(context, R.attr.cardBackgroundColor));
+
+ innerPaint = new Paint();
+ innerPaint.setAntiAlias(true);
+
+ outerPaint = new Paint();
+ outerPaint.setAntiAlias(true);
+
+ setWillNotDraw(false);
+ }
+
+ private void update(@ColorInt int color) {
+ innerPaint.setColor(color);
+ outerPaint.setColor(ColorUtil.shiftColorDown(color));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ int[][] states = new int[][]{
+ new int[]{android.R.attr.state_pressed}
+ };
+ int[] colors = new int[]{createSelectedColor(color)};
+ ColorStateList rippleColors = new ColorStateList(states, colors);
+ Drawable mask = new ShapeDrawable(new OvalShape());
+ RippleDrawable rippleDrawable = new RippleDrawable(rippleColors, null, mask);
+
+ setForeground(rippleDrawable);
+ } else {
+ ShapeDrawable pressedDrawable = new ShapeDrawable(new OvalShape());
+ pressedDrawable.getPaint().setColor(createSelectedColor(color));
+ StateListDrawable stateListDrawable = new StateListDrawable();
+ stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDrawable);
+
+ setForeground(stateListDrawable);
+ }
+ }
+
+ @ColorInt
+ private static int createSelectedColor(int color) {
+ if (ColorUtil.useDarkTextColorOnBackground(color)) {
+ return ColorUtil.shiftColor(color, 0.8f);
+ } else {
+ return ColorUtil.shiftColor(color, 1.2f);
+ }
+ }
+
+ @Override
+ public void setBackgroundColor(@ColorInt int color) {
+ update(color);
+ requestLayout();
+ invalidate();
+ }
+
+ @Override
+ public void setBackgroundResource(@ColorRes int color) {
+ setBackgroundColor(ContextCompat.getColor(getContext(), color));
+ }
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ @Override
+ public void setBackground(Drawable background) {
+ throw new IllegalStateException("Cannot use setBackground() on CircleView.");
+ }
+
+ /**
+ * @deprecated
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ @Override
+ public void setBackgroundDrawable(Drawable background) {
+ throw new IllegalStateException("Cannot use setBackgroundDrawable() on CircleView.");
+ }
+
+ /**
+ * @deprecated
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ @Override
+ public void setActivated(boolean activated) {
+ throw new IllegalStateException("Cannot use setActivated() on CircleView.");
+ }
+
+ public void setSelected(boolean selected) {
+ mSelected = selected;
+ requestLayout();
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ //noinspection SuspiciousNameCombination
+ int height = width;
+ if (heightMode == MeasureSpec.AT_MOST)
+ height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
+ setMeasuredDimension(width, height);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ final int outerRadius = getMeasuredWidth() / 2;
+ if (mSelected) {
+ final int whiteRadius = outerRadius - borderWidthLarge;
+ final int innerRadius = whiteRadius - borderWidthSmall;
+ canvas.drawCircle(getMeasuredWidth() / 2,
+ getMeasuredHeight() / 2,
+ outerRadius,
+ outerPaint);
+ canvas.drawCircle(getMeasuredWidth() / 2,
+ getMeasuredHeight() / 2,
+ whiteRadius,
+ gapPaint);
+ canvas.drawCircle(getMeasuredWidth() / 2,
+ getMeasuredHeight() / 2,
+ innerRadius,
+ innerPaint);
+ } else {
+ canvas.drawCircle(getMeasuredWidth() / 2,
+ getMeasuredHeight() / 2,
+ outerRadius,
+ innerPaint);
+ }
+ }
+}
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
deleted file mode 100644
index 093fc062..00000000
--- a/app/src/main/res/values/arrays.xml
+++ /dev/null
@@ -1,261 +0,0 @@
-