mirror of https://github.com/tasks/tasks
New palette and wheel color pickers
parent
78deb5c0d6
commit
4faffdd905
@ -1,119 +0,0 @@
|
||||
package org.tasks.activities;
|
||||
|
||||
import static org.tasks.billing.PurchaseDialog.newPurchaseDialog;
|
||||
import static org.tasks.dialogs.ColorPickerDialog.newColorPickerDialog;
|
||||
import static org.tasks.themes.ThemeColor.newThemeColor;
|
||||
|
||||
import android.content.Intent;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.tasks.R;
|
||||
import org.tasks.billing.Inventory;
|
||||
import org.tasks.dialogs.ColorPickerDialog;
|
||||
import org.tasks.dialogs.ColorPickerDialog.Pickable;
|
||||
import org.tasks.injection.ActivityComponent;
|
||||
import org.tasks.injection.InjectingAppCompatActivity;
|
||||
import org.tasks.preferences.Preferences;
|
||||
import org.tasks.themes.Theme;
|
||||
import org.tasks.themes.ThemeCache;
|
||||
|
||||
public class ColorPickerActivity extends InjectingAppCompatActivity
|
||||
implements ColorPickerDialog.ThemePickerCallback {
|
||||
|
||||
public static final String EXTRA_PALETTE = "extra_palette";
|
||||
public static final String EXTRA_SHOW_NONE = "extra_show_none";
|
||||
public static final String EXTRA_COLOR = "extra_index";
|
||||
private static final String FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker";
|
||||
private static final String FRAG_TAG_PURCHASE = "frag_tag_purchase";
|
||||
@Inject Theme theme;
|
||||
@Inject ThemeCache themeCache;
|
||||
@Inject Inventory inventory;
|
||||
@Inject Preferences preferences;
|
||||
|
||||
private ColorPalette palette;
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
|
||||
Intent intent = getIntent();
|
||||
palette = (ColorPalette) intent.getSerializableExtra(EXTRA_PALETTE);
|
||||
boolean showNone = intent.getBooleanExtra(EXTRA_SHOW_NONE, false);
|
||||
List<? extends Pickable> items = getItems(palette);
|
||||
int selected;
|
||||
if (palette == ColorPalette.COLORS) {
|
||||
selected =
|
||||
items.indexOf(
|
||||
intent.hasExtra(EXTRA_COLOR)
|
||||
? newThemeColor(this, intent.getIntExtra(EXTRA_COLOR, 0))
|
||||
: theme.getThemeColor());
|
||||
} else {
|
||||
selected =
|
||||
intent.hasExtra(EXTRA_COLOR)
|
||||
? intent.getIntExtra(EXTRA_COLOR, -1)
|
||||
: getCurrentSelection(palette);
|
||||
}
|
||||
|
||||
newColorPickerDialog(items, showNone, selected)
|
||||
.show(getSupportFragmentManager(), FRAG_TAG_COLOR_PICKER);
|
||||
}
|
||||
|
||||
private List<? extends ColorPickerDialog.Pickable> getItems(ColorPalette palette) {
|
||||
switch (palette) {
|
||||
case ACCENTS:
|
||||
return themeCache.getAccents();
|
||||
case COLORS:
|
||||
return themeCache.getColors();
|
||||
case LAUNCHER:
|
||||
return themeCache.getColors().subList(0, themeCache.getColors().size() - 1);
|
||||
case WIDGET_BACKGROUND:
|
||||
return themeCache.getWidgetThemes();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported palette: " + palette);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void themePicked(ColorPickerDialog.Pickable picked) {
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EXTRA_PALETTE, palette);
|
||||
data.putExtra(EXTRA_COLOR, picked == null ? -1 : picked.getIndex());
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initiateThemePurchase() {
|
||||
newPurchaseDialog().show(getSupportFragmentManager(), FRAG_TAG_PURCHASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private int getCurrentSelection(ColorPalette palette) {
|
||||
switch (palette) {
|
||||
case COLORS:
|
||||
return theme.getThemeColor().getIndex();
|
||||
case ACCENTS:
|
||||
return theme.getThemeAccent().getIndex();
|
||||
case LAUNCHER:
|
||||
return preferences.getInt(R.string.p_theme_launcher, 7);
|
||||
default:
|
||||
return theme.getThemeBase().getIndex();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ColorPalette {
|
||||
COLORS,
|
||||
ACCENTS,
|
||||
LAUNCHER,
|
||||
WIDGET_BACKGROUND
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.tasks.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import org.tasks.Callback
|
||||
import org.tasks.R
|
||||
import org.tasks.billing.Inventory
|
||||
import org.tasks.billing.PurchaseDialog
|
||||
import org.tasks.injection.DialogFragmentComponent
|
||||
import org.tasks.injection.InjectingDialogFragment
|
||||
import org.tasks.themes.ThemeCache
|
||||
import org.tasks.themes.ThemeColor
|
||||
import javax.inject.Inject
|
||||
|
||||
class ColorPalettePicker : InjectingDialogFragment() {
|
||||
|
||||
companion object {
|
||||
private const val FRAG_TAG_PURCHASE = "frag_tag_purchase"
|
||||
private const val EXTRA_PALETTE = "extra_palette"
|
||||
const val EXTRA_SELECTED = ColorWheelPicker.EXTRA_SELECTED
|
||||
|
||||
fun newColorPalette(
|
||||
target: Fragment?,
|
||||
rc: Int,
|
||||
palette: ColorPickerAdapter.Palette
|
||||
): ColorPalettePicker {
|
||||
val args = Bundle()
|
||||
args.putSerializable(EXTRA_PALETTE, palette)
|
||||
val dialog = ColorPalettePicker()
|
||||
dialog.setTargetFragment(target, rc)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
||||
interface Pickable : Parcelable {
|
||||
val pickerColor: Int
|
||||
val isFree: Boolean
|
||||
val index: Int
|
||||
}
|
||||
|
||||
interface ColorPickedCallback {
|
||||
fun onColorPicked(index: Int)
|
||||
}
|
||||
|
||||
@Inject lateinit var dialogBuilder: DialogBuilder
|
||||
@Inject lateinit var inventory: Inventory
|
||||
@Inject lateinit var themeCache: ThemeCache
|
||||
|
||||
@BindView(R.id.icons) lateinit var recyclerView: RecyclerView
|
||||
|
||||
lateinit var colors: List<Pickable>
|
||||
lateinit var palette: ColorPickerAdapter.Palette
|
||||
var callback: ColorPickedCallback? = null
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.dialog_icon_picker, null)
|
||||
ButterKnife.bind(this, view)
|
||||
palette = arguments!!.getSerializable(EXTRA_PALETTE) as ColorPickerAdapter.Palette
|
||||
colors = when (palette) {
|
||||
ColorPickerAdapter.Palette.COLORS -> themeCache.colors
|
||||
ColorPickerAdapter.Palette.ACCENTS -> themeCache.accents
|
||||
ColorPickerAdapter.Palette.LAUNCHERS -> themeCache.colors.dropLast(1)
|
||||
ColorPickerAdapter.Palette.WIDGET_BACKGROUND -> themeCache.widgetThemes
|
||||
}
|
||||
|
||||
val iconPickerAdapter = ColorPickerAdapter(
|
||||
context as Activity,
|
||||
inventory,
|
||||
Callback { index: Int -> onSelected(index) })
|
||||
recyclerView.layoutManager = IconLayoutManager(context)
|
||||
recyclerView.adapter = iconPickerAdapter
|
||||
iconPickerAdapter.submitList(colors)
|
||||
val builder =
|
||||
dialogBuilder
|
||||
.newDialog()
|
||||
.setView(view)
|
||||
if (inventory.hasPro()) {
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
} else {
|
||||
builder.setPositiveButton(R.string.button_subscribe) { _: DialogInterface?, _: Int ->
|
||||
PurchaseDialog.newPurchaseDialog().show(parentFragmentManager, FRAG_TAG_PURCHASE)
|
||||
}
|
||||
}
|
||||
return builder.show()
|
||||
}
|
||||
|
||||
override fun onAttach(activity: Activity) {
|
||||
super.onAttach(activity)
|
||||
|
||||
if (activity is ColorPickedCallback) {
|
||||
callback = activity
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSelected(index: Int) {
|
||||
val result = when (palette) {
|
||||
ColorPickerAdapter.Palette.COLORS ->
|
||||
(colors.find { it.index == index } as ThemeColor).primaryColor
|
||||
else -> index
|
||||
}
|
||||
dialog?.dismiss()
|
||||
if (targetFragment == null) {
|
||||
callback?.onColorPicked(result)
|
||||
} else {
|
||||
val data = Intent().putExtra(EXTRA_SELECTED, result)
|
||||
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun inject(component: DialogFragmentComponent) = component.inject(this)
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org.tasks.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import org.tasks.Callback
|
||||
import org.tasks.R
|
||||
import org.tasks.billing.Inventory
|
||||
|
||||
class ColorPickerAdapter(
|
||||
private val activity: Activity,
|
||||
private val inventory: Inventory,
|
||||
private val onSelected: Callback<Int>
|
||||
) : ListAdapter<ColorPalettePicker.Pickable, IconPickerHolder>(DiffCallback()) {
|
||||
|
||||
enum class Palette {
|
||||
COLORS,
|
||||
ACCENTS,
|
||||
LAUNCHERS,
|
||||
WIDGET_BACKGROUND
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IconPickerHolder {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_icon_picker_cell, parent, false)
|
||||
return IconPickerHolder(activity, view, onSelected)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: IconPickerHolder, position: Int) {
|
||||
val pickable = getItem(position)
|
||||
val available = inventory.hasPro() || pickable.isFree
|
||||
holder.bind(
|
||||
pickable.index,
|
||||
if (available) R.drawable.color_picker else R.drawable.ic_outline_vpn_key_24px,
|
||||
pickable.pickerColor,
|
||||
1f,
|
||||
available
|
||||
)
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<ColorPalettePicker.Pickable>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: ColorPalettePicker.Pickable,
|
||||
newItem: ColorPalettePicker.Pickable
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ColorPalettePicker.Pickable,
|
||||
newItem: ColorPalettePicker.Pickable
|
||||
): Boolean {
|
||||
return oldItem.index == newItem.index
|
||||
}
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
package org.tasks.dialogs;
|
||||
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
import static com.google.common.collect.Lists.transform;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.tasks.R;
|
||||
import org.tasks.billing.Inventory;
|
||||
import org.tasks.injection.DialogFragmentComponent;
|
||||
import org.tasks.injection.ForActivity;
|
||||
import org.tasks.injection.InjectingDialogFragment;
|
||||
import org.tasks.preferences.Preferences;
|
||||
import org.tasks.themes.Theme;
|
||||
import org.tasks.ui.SingleCheckedArrayAdapter;
|
||||
|
||||
public class ColorPickerDialog extends InjectingDialogFragment {
|
||||
|
||||
private static final String EXTRA_ITEMS = "extra_items";
|
||||
private static final String EXTRA_SELECTED = "extra_selected";
|
||||
private static final String EXTRA_SHOW_NONE = "extra_show_none";
|
||||
@Inject DialogBuilder dialogBuilder;
|
||||
@Inject @ForActivity Context context;
|
||||
@Inject Preferences preferences;
|
||||
@Inject Theme theme;
|
||||
@Inject Inventory inventory;
|
||||
private ThemePickerCallback callback;
|
||||
private SingleCheckedArrayAdapter adapter;
|
||||
private Dialog dialog;
|
||||
|
||||
public static ColorPickerDialog newColorPickerDialog(
|
||||
List<? extends Pickable> items, boolean showNone, int selection) {
|
||||
ColorPickerDialog dialog = new ColorPickerDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelableArrayList(EXTRA_ITEMS, new ArrayList<Pickable>(items));
|
||||
args.putInt(EXTRA_SELECTED, selection);
|
||||
args.putBoolean(EXTRA_SHOW_NONE, showNone);
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
Bundle arguments = getArguments();
|
||||
final List<Pickable> items = arguments.getParcelableArrayList(EXTRA_ITEMS);
|
||||
boolean showNone = arguments.getBoolean(EXTRA_SHOW_NONE);
|
||||
int selected = arguments.getInt(EXTRA_SELECTED, -1);
|
||||
|
||||
adapter =
|
||||
new SingleCheckedArrayAdapter(
|
||||
context, transform(items, Pickable::getName), theme.getThemeAccent()) {
|
||||
@Override
|
||||
protected int getDrawable(int position) {
|
||||
return inventory.purchasedThemes() || items.get(position).isFree()
|
||||
? R.drawable.ic_baseline_lens_24px
|
||||
: R.drawable.ic_outline_vpn_key_24px;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDrawableColor(int position) {
|
||||
int pickerColor = items.get(position).getPickerColor();
|
||||
return pickerColor == -1 ? getColor(context, R.color.grey_50) : pickerColor;
|
||||
}
|
||||
};
|
||||
|
||||
AlertDialogBuilder builder =
|
||||
dialogBuilder
|
||||
.newDialog()
|
||||
.setSingleChoiceItems(
|
||||
adapter,
|
||||
selected,
|
||||
(dialog, which) -> {
|
||||
Pickable picked = items.get(which);
|
||||
if (inventory.purchasedThemes() || picked.isFree()) {
|
||||
callback.themePicked(picked);
|
||||
} else {
|
||||
callback.initiateThemePurchase();
|
||||
}
|
||||
})
|
||||
.setOnDismissListener(dialogInterface -> callback.dismissed());
|
||||
if (showNone) {
|
||||
builder.setNeutralButton(R.string.none, (dialogInterface, i) -> callback.themePicked(null));
|
||||
}
|
||||
dialog = builder.create();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
callback.dismissed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
callback = (ThemePickerCallback) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(DialogFragmentComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
public interface Pickable extends Parcelable {
|
||||
|
||||
String getName();
|
||||
|
||||
int getPickerColor();
|
||||
|
||||
boolean isFree();
|
||||
|
||||
int getIndex();
|
||||
}
|
||||
|
||||
public interface ThemePickerCallback {
|
||||
|
||||
void themePicked(Pickable pickable);
|
||||
|
||||
void initiateThemePurchase();
|
||||
|
||||
void dismissed();
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package org.tasks.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_CANCELED
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.flask.colorpicker.ColorPickerView
|
||||
import com.flask.colorpicker.builder.ColorPickerDialogBuilder
|
||||
import org.tasks.R
|
||||
import org.tasks.billing.Inventory
|
||||
import org.tasks.billing.PurchaseActivity
|
||||
import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette
|
||||
import org.tasks.injection.DialogFragmentComponent
|
||||
import org.tasks.injection.InjectingDialogFragment
|
||||
import org.tasks.ui.NavigationDrawerFragment.REQUEST_PURCHASE
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val REQUEST_PURCHASE = 10010
|
||||
|
||||
class ColorWheelPicker : InjectingDialogFragment() {
|
||||
|
||||
companion object {
|
||||
private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"
|
||||
const val EXTRA_SELECTED = "extra_selected"
|
||||
|
||||
fun newColorWheel(target: Fragment?, rc: Int, selected: Int): ColorWheelPicker {
|
||||
val args = Bundle()
|
||||
args.putInt(EXTRA_SELECTED, selected)
|
||||
val dialog = ColorWheelPicker()
|
||||
dialog.setTargetFragment(target, rc)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
||||
interface ColorPickedCallback {
|
||||
fun onColorPicked(color: Int)
|
||||
}
|
||||
|
||||
@Inject lateinit var inventory: Inventory
|
||||
|
||||
var dialog: AlertDialog? = null
|
||||
var selected = -1
|
||||
var callback: ColorPickedCallback? = null
|
||||
|
||||
override fun inject(component: DialogFragmentComponent) = component.inject(this)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
selected = savedInstanceState?.getInt(EXTRA_SELECTED) ?: arguments!!.getInt(EXTRA_SELECTED, 0)
|
||||
|
||||
val button = if (inventory.hasPro()) android.R.string.ok else R.string.button_subscribe
|
||||
val builder = ColorPickerDialogBuilder
|
||||
.with(activity)
|
||||
.wheelType(ColorPickerView.WHEEL_TYPE.CIRCLE)
|
||||
.density(7)
|
||||
.setOnColorChangedListener { which ->
|
||||
selected = which
|
||||
}
|
||||
.setOnColorSelectedListener { which ->
|
||||
selected = which
|
||||
}
|
||||
.lightnessSliderOnly()
|
||||
.setPositiveButton(button) { _, _, _ ->
|
||||
if (inventory.hasPro()) {
|
||||
deliverSelection()
|
||||
} else {
|
||||
startActivityForResult(Intent(activity, PurchaseActivity::class.java), REQUEST_PURCHASE)
|
||||
}
|
||||
}
|
||||
if (selected != 0) {
|
||||
builder.initialColor(selected)
|
||||
}
|
||||
val buttonText = if (inventory.hasPro()) R.string.material_palette else R.string.free_colors
|
||||
builder.setNegativeButton(buttonText) { _, _ ->
|
||||
newColorPalette(targetFragment, targetRequestCode, ColorPickerAdapter.Palette.COLORS)
|
||||
.show(parentFragmentManager, FRAG_TAG_COLOR_PICKER)
|
||||
}
|
||||
dialog = builder.build()
|
||||
return dialog as Dialog
|
||||
}
|
||||
|
||||
override fun onAttach(activity: Activity) {
|
||||
super.onAttach(activity)
|
||||
|
||||
if (activity is ColorPickedCallback) {
|
||||
callback = activity
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == org.tasks.dialogs.REQUEST_PURCHASE) {
|
||||
if (inventory.hasPro()) {
|
||||
deliverSelection()
|
||||
} else {
|
||||
dialog?.cancel()
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deliverSelection() {
|
||||
dialog?.dismiss()
|
||||
if (targetFragment == null) {
|
||||
callback?.onColorPicked(selected)
|
||||
} else {
|
||||
val data = Intent().putExtra(EXTRA_SELECTED, selected)
|
||||
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
targetFragment?.onActivityResult(targetRequestCode, RESULT_CANCELED, null)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
outState.putInt(EXTRA_SELECTED, selected)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue