diff --git a/app/src/main/java/org/tasks/tasklist/TagFormatter.java b/app/src/main/java/org/tasks/tasklist/TagFormatter.java deleted file mode 100644 index 06af6e1f9..000000000 --- a/app/src/main/java/org/tasks/tasklist/TagFormatter.java +++ /dev/null @@ -1,219 +0,0 @@ -package org.tasks.tasklist; - -import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Lists.transform; - -import android.content.Context; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.BackgroundColorSpan; -import android.text.style.ForegroundColorSpan; -import android.util.TypedValue; -import com.google.common.base.Function; -import com.google.common.base.Predicates; -import com.google.common.base.Strings; -import com.google.common.collect.Ordering; -import com.todoroo.astrid.tags.TagService; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import org.tasks.R; -import org.tasks.data.CaldavCalendar; -import org.tasks.data.CaldavDao; -import org.tasks.data.GoogleTaskList; -import org.tasks.data.GoogleTaskListDao; -import org.tasks.data.TagData; -import org.tasks.injection.ForApplication; -import org.tasks.themes.ThemeCache; -import org.tasks.themes.ThemeColor; - -public class TagFormatter { - - private static final char SPACE = '\u0020'; - private static final char HAIR_SPACE = '\u200a'; - private static final int MAX_TAGS = 4; - - private final Map tagMap = new HashMap<>(); - private final Map googleTaskLists = new HashMap<>(); - private final Map caldavCalendars = new HashMap<>(); - private final TagService tagService; - private final ThemeCache themeCache; - private final GoogleTaskListDao googleTaskListDao; - private final CaldavDao caldavDao; - private final float tagCharacters; - private final Ordering orderByName = - new Ordering() { - @Override - public int compare(ColoredString left, ColoredString right) { - return left.name.compareTo(right.name); - } - }; - private final Ordering orderByLength = - new Ordering() { - @Override - public int compare(ColoredString left, ColoredString right) { - int leftLength = left.name.length(); - int rightLength = right.name.length(); - if (leftLength < rightLength) { - return -1; - } else if (rightLength < leftLength) { - return 1; - } else { - return 0; - } - } - }; - - @Inject - public TagFormatter( - @ForApplication Context context, - TagService tagService, - ThemeCache themeCache, - GoogleTaskListDao googleTaskListDao, - CaldavDao caldavDao) { - this.tagService = tagService; - this.themeCache = themeCache; - this.googleTaskListDao = googleTaskListDao; - this.caldavDao = caldavDao; - - TypedValue typedValue = new TypedValue(); - context.getResources().getValue(R.dimen.tag_characters, typedValue, true); - tagCharacters = typedValue.getFloat(); - - for (TagData tagData : tagService.getTagList()) { - tagMap.put(tagData.getRemoteId(), new ColoredString(tagData)); - } - for (CaldavCalendar calendar : caldavDao.getCalendars()) { - caldavCalendars.put(calendar.getUuid(), new ColoredString(calendar)); - } - for (GoogleTaskList list : googleTaskListDao.getAllLists()) { - googleTaskLists.put(list.getRemoteId(), new ColoredString(list)); - } - } - - CharSequence getTagString(String caldav, String googleTask, List tagUuids) { - List strings = new ArrayList<>(); - if (!Strings.isNullOrEmpty(googleTask)) { - ColoredString googleTaskList = getGoogleTaskList(googleTask); - if (googleTaskList != null) { - strings.add(googleTaskList); - } - } else if (!Strings.isNullOrEmpty(caldav)) { - ColoredString caldavCalendar = getCaldavCalendar(caldav); - if (caldavCalendar != null) { - strings.add(caldavCalendar); - } - } - - Iterable tags = filter(transform(tagUuids, this::getTag), Predicates.notNull()); - strings.addAll(0, orderByName.leastOf(tags, MAX_TAGS - strings.size())); - int numTags = strings.size(); - if (numTags == 0) { - return null; - } - List firstFourByNameLength = orderByLength.sortedCopy(strings); - float maxLength = tagCharacters / numTags; - for (int i = 0; i < numTags - 1; i++) { - ColoredString tagData = firstFourByNameLength.get(i); - String name = tagData.name; - if (name.length() >= maxLength) { - break; - } - float excess = maxLength - name.length(); - int beneficiaries = numTags - i - 1; - float additional = excess / beneficiaries; - maxLength += additional; - } - List tagStrings = transform(strings, tagToString(maxLength)); - SpannableStringBuilder builder = new SpannableStringBuilder(); - for (SpannableString tagString : tagStrings) { - if (builder.length() > 0) { - builder.append(HAIR_SPACE); - } - builder.append(tagString); - } - return builder; - } - - private Function tagToString(final float maxLength) { - return tagData -> { - String tagName = tagData.name; - tagName = tagName.substring(0, Math.min(tagName.length(), (int) maxLength)); - SpannableString string = new SpannableString(SPACE + tagName + SPACE); - int themeIndex = tagData.color; - ThemeColor color = - themeIndex >= 0 ? themeCache.getThemeColor(themeIndex) : themeCache.getUntaggedColor(); - string.setSpan( - new BackgroundColorSpan(color.getPrimaryColor()), - 0, - string.length(), - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - string.setSpan( - new ForegroundColorSpan(color.getActionBarTint()), - 0, - string.length(), - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return string; - }; - } - - private ColoredString getGoogleTaskList(String remoteId) { - ColoredString googleTaskList = googleTaskLists.get(remoteId); - if (googleTaskList == null) { - GoogleTaskList byRemoteId = googleTaskListDao.getByRemoteId(remoteId); - if (byRemoteId != null) { - googleTaskList = new ColoredString(byRemoteId); - } - googleTaskLists.put(remoteId, googleTaskList); - } - return googleTaskList; - } - - private ColoredString getCaldavCalendar(String uuid) { - ColoredString calendar = caldavCalendars.get(uuid); - if (calendar == null) { - CaldavCalendar byUuid = caldavDao.getCalendar(uuid); - if (byUuid != null) { - calendar = new ColoredString(byUuid); - } - caldavCalendars.put(uuid, calendar); - } - return calendar; - } - - private ColoredString getTag(String uuid) { - ColoredString tagData = tagMap.get(uuid); - if (tagData == null) { - TagData tagByUuid = tagService.getTagByUuid(uuid); - if (tagByUuid != null) { - tagData = new ColoredString(tagByUuid); - } - tagMap.put(uuid, tagData); - } - return tagData; - } - - private class ColoredString { - - final String name; - final int color; - - ColoredString(TagData tagData) { - name = tagData.getName(); - color = tagData.getColor(); - } - - ColoredString(GoogleTaskList googleTaskList) { - name = googleTaskList.getTitle(); - color = googleTaskList.getColor(); - } - - ColoredString(CaldavCalendar caldavCalendar) { - name = caldavCalendar.getName(); - color = caldavCalendar.getColor(); - } - } -} diff --git a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java index b574b27d7..4cd79fe69 100644 --- a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java +++ b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java @@ -10,8 +10,10 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.ItemTouchHelper; import android.view.ViewGroup; import com.google.common.primitives.Longs; +import com.todoroo.astrid.activity.MainActivity; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.adapter.TaskAdapter; +import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.utility.Flags; import java.util.List; @@ -108,6 +110,14 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter } } + @Override + public void onClick(Filter filter) { + if (mode == null) { + MainActivity activity = (MainActivity) taskList.getActivity(); + activity.onFilterItemClicked(filter); + } + } + @Override public boolean onLongPress(ViewHolder viewHolder) { if (!adapter.isManuallySorted()) { diff --git a/app/src/main/java/org/tasks/tasklist/ViewHolder.java b/app/src/main/java/org/tasks/tasklist/ViewHolder.java index 5c96f5e65..0afc6551b 100644 --- a/app/src/main/java/org/tasks/tasklist/ViewHolder.java +++ b/app/src/main/java/org/tasks/tasklist/ViewHolder.java @@ -10,15 +10,21 @@ import android.graphics.Paint; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.HorizontalScrollView; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.OnLongClick; +import com.google.android.material.chip.Chip; +import com.google.android.material.chip.ChipGroup; import com.google.common.collect.Lists; import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.activity.MainActivity; +import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.ui.CheckableImageView; @@ -26,13 +32,13 @@ import java.util.List; import org.tasks.R; import org.tasks.preferences.Preferences; import org.tasks.ui.CheckBoxes; +import org.tasks.ui.ChipProvider; class ViewHolder extends RecyclerView.ViewHolder { private final Context context; private final Preferences preferences; private final CheckBoxes checkBoxes; - private final TagFormatter tagFormatter; private final int textColorSecondary; private final TaskDao taskDao; private final ViewHolderCallbacks callback; @@ -40,6 +46,8 @@ class ViewHolder extends RecyclerView.ViewHolder { private final int background; private final int selectedColor; private final int textColorOverdue; + private final ChipProvider chipProvider; + private final int fontSizeDetails; @BindView(R.id.row) public ViewGroup row; @@ -61,8 +69,11 @@ class ViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.completeBox) CheckableImageView completeBox; - @BindView(R.id.tag_block) - TextView tagBlock; + @BindView(R.id.chip_scroll) + HorizontalScrollView chipScroll; + + @BindView(R.id.chip_group) + ChipGroup chipGroup; private int indent; private boolean selected; @@ -74,7 +85,7 @@ class ViewHolder extends RecyclerView.ViewHolder { Preferences preferences, int fontSize, CheckBoxes checkBoxes, - TagFormatter tagFormatter, + ChipProvider chipProvider, int textColorOverdue, int textColorSecondary, TaskDao taskDao, @@ -87,7 +98,7 @@ class ViewHolder extends RecyclerView.ViewHolder { this.context = context; this.preferences = preferences; this.checkBoxes = checkBoxes; - this.tagFormatter = tagFormatter; + this.chipProvider = chipProvider; this.textColorOverdue = textColorOverdue; this.textColorSecondary = textColorSecondary; this.taskDao = taskDao; @@ -119,9 +130,9 @@ class ViewHolder extends RecyclerView.ViewHolder { } nameView.setTextSize(fontSize); - int fontSizeDetails = Math.max(10, fontSize - 2); + description.setTextSize(fontSize); + fontSizeDetails = Math.max(10, fontSize - 2); dueDate.setTextSize(fontSizeDetails); - tagBlock.setTextSize(fontSizeDetails); view.setTag(this); for (int i = 0; i < view.getChildCount(); i++) { @@ -248,19 +259,20 @@ class ViewHolder extends RecyclerView.ViewHolder { dueDateView.setVisibility(View.GONE); } - if (task.isCompleted()) { - tagBlock.setVisibility(View.GONE); + String tags = task.getTagsString(); + List tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList(); + + List chips = chipProvider.getChips(task.getCaldav(), task.getGoogleTaskList(), tagUuids); + if (chips.isEmpty()) { + chipScroll.setVisibility(View.GONE); } else { - String tags = task.getTagsString(); - List tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList(); - CharSequence tagString = - tagFormatter.getTagString(task.getCaldav(), task.getGoogleTaskList(), tagUuids); - if (TextUtils.isEmpty(tagString)) { - tagBlock.setVisibility(View.GONE); - } else { - tagBlock.setText(tagString); - tagBlock.setVisibility(View.VISIBLE); + chipGroup.removeAllViews(); + for (Chip chip : chips) { + chip.setTextSize(fontSizeDetails); + chip.setOnClickListener(view -> callback.onClick((Filter) view.getTag())); + chipGroup.addView(chip); } + chipScroll.setVisibility(View.VISIBLE); } } @@ -297,6 +309,8 @@ class ViewHolder extends RecyclerView.ViewHolder { void onClick(ViewHolder viewHolder); + void onClick(Filter filter); + boolean onLongPress(ViewHolder viewHolder); } } diff --git a/app/src/main/java/org/tasks/tasklist/ViewHolderFactory.java b/app/src/main/java/org/tasks/tasklist/ViewHolderFactory.java index ad991ca2f..724adc3fa 100644 --- a/app/src/main/java/org/tasks/tasklist/ViewHolderFactory.java +++ b/app/src/main/java/org/tasks/tasklist/ViewHolderFactory.java @@ -15,6 +15,7 @@ import org.tasks.R; import org.tasks.injection.ForActivity; import org.tasks.preferences.Preferences; import org.tasks.ui.CheckBoxes; +import org.tasks.ui.ChipProvider; public class ViewHolderFactory { @@ -22,7 +23,7 @@ public class ViewHolderFactory { private final int textColorOverdue; private final Context context; private final CheckBoxes checkBoxes; - private final TagFormatter tagFormatter; + private final ChipProvider chipProvider; private final int fontSize; private final TaskDao taskDao; private final DisplayMetrics metrics; @@ -36,11 +37,11 @@ public class ViewHolderFactory { @ForActivity Context context, Preferences preferences, CheckBoxes checkBoxes, - TagFormatter tagFormatter, + ChipProvider chipProvider, TaskDao taskDao) { this.context = context; this.checkBoxes = checkBoxes; - this.tagFormatter = tagFormatter; + this.chipProvider = chipProvider; this.taskDao = taskDao; this.preferences = preferences; textColorSecondary = getData(context, android.R.attr.textColorSecondary); @@ -60,7 +61,7 @@ public class ViewHolderFactory { preferences, fontSize, checkBoxes, - tagFormatter, + chipProvider, textColorOverdue, textColorSecondary, taskDao, diff --git a/app/src/main/java/org/tasks/ui/ChipProvider.java b/app/src/main/java/org/tasks/ui/ChipProvider.java index 3d94fd571..944f08a71 100644 --- a/app/src/main/java/org/tasks/ui/ChipProvider.java +++ b/app/src/main/java/org/tasks/ui/ChipProvider.java @@ -1,16 +1,32 @@ package org.tasks.ui; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; import static org.tasks.preferences.ResourceResolver.getDimen; +import android.app.Activity; import android.content.Context; import android.content.res.ColorStateList; +import android.view.LayoutInflater; import com.google.android.material.chip.Chip; +import com.google.common.base.Predicates; +import com.google.common.base.Strings; import com.google.common.collect.Ordering; +import com.todoroo.astrid.api.CaldavFilter; import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.api.GtasksFilter; +import com.todoroo.astrid.api.TagFilter; +import com.todoroo.astrid.tags.TagService; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.inject.Inject; import org.tasks.R; +import org.tasks.data.CaldavCalendar; +import org.tasks.data.CaldavDao; +import org.tasks.data.GoogleTaskList; +import org.tasks.data.GoogleTaskListDao; import org.tasks.data.TagData; import org.tasks.injection.ForActivity; import org.tasks.themes.ThemeCache; @@ -18,14 +34,35 @@ import org.tasks.themes.ThemeColor; public class ChipProvider { + private final Map googleTaskLists = new HashMap<>(); + private final Map caldavCalendars = new HashMap<>(); + private final Map tagDatas = new HashMap<>(); private final Context context; private final ThemeCache themeCache; private final int iconAlpha; + private final GoogleTaskListDao googleTaskListDao; + private final CaldavDao caldavDao; + private final TagService tagService; + private final Ordering orderByName = + new Ordering() { + @Override + public int compare(TagFilter left, TagFilter right) { + return left.listingTitle.compareTo(right.listingTitle); + } + }; @Inject - public ChipProvider(@ForActivity Context context, ThemeCache themeCache) { + public ChipProvider( + @ForActivity Context context, + ThemeCache themeCache, + GoogleTaskListDao googleTaskListDao, + CaldavDao caldavDao, + TagService tagService) { this.context = context; this.themeCache = themeCache; + this.googleTaskListDao = googleTaskListDao; + this.caldavDao = caldavDao; + this.tagService = tagService; iconAlpha = (int) (255 * getDimen(context, R.dimen.alpha_secondary)); } @@ -36,10 +73,39 @@ public class ChipProvider { return chip; } + public List getChips(String caldav, String googleTask, Iterable tagUuids) { + List chips = new ArrayList<>(); + if (!Strings.isNullOrEmpty(googleTask)) { + GtasksFilter googleTaskFilter = getGoogleTaskList(googleTask); + if (googleTaskFilter != null) { + chips.add(newChip(googleTaskFilter)); + } + } else if (!Strings.isNullOrEmpty(caldav)) { + CaldavFilter caldavFilter = getCaldavCalendar(caldav); + if (caldavFilter != null) { + chips.add(newChip(caldavFilter)); + } + } + Iterable tagFilters = filter(transform(tagUuids, this::getTag), Predicates.notNull()); + for (TagFilter tagFilter : orderByName.sortedCopy(tagFilters)) { + chips.add(newChip(tagFilter)); + } + + return chips; + } + public void apply(Chip chip, Filter filter) { apply(chip, filter.listingTitle, filter.tint); } + private Chip newChip(Filter filter) { + LayoutInflater layoutInflater = ((Activity) context).getLayoutInflater(); + Chip chip = (Chip) layoutInflater.inflate(R.layout.chip_task_list, null); + chip.setTag(filter); + apply(chip, filter.listingTitle, filter.tint); + return chip; + } + private void apply(Chip chip, String name, int theme) { ThemeColor color = theme >= 0 ? themeCache.getThemeColor(theme) : themeCache.getUntaggedColor(); chip.setText(name); @@ -54,4 +120,40 @@ public class ChipProvider { }, new int[] {color.getPrimaryColor(), color.getPrimaryColor()})); } + + private GtasksFilter getGoogleTaskList(String remoteId) { + GtasksFilter gtasksFilter = googleTaskLists.get(remoteId); + if (gtasksFilter == null) { + GoogleTaskList googleTaskList = googleTaskListDao.getByRemoteId(remoteId); + if (googleTaskList != null) { + gtasksFilter = new GtasksFilter(googleTaskList); + googleTaskLists.put(remoteId, gtasksFilter); + } + } + return gtasksFilter; + } + + private CaldavFilter getCaldavCalendar(String uuid) { + CaldavFilter caldavFilter = caldavCalendars.get(uuid); + if (caldavFilter == null) { + CaldavCalendar calendar = caldavDao.getCalendar(uuid); + if (calendar != null) { + caldavFilter = new CaldavFilter(calendar); + caldavCalendars.put(uuid, caldavFilter); + } + } + return caldavFilter; + } + + private TagFilter getTag(String uuid) { + TagFilter tagFilter = tagDatas.get(uuid); + if (tagFilter == null) { + TagData tagData = tagService.getTagByUuid(uuid); + if (tagData != null) { + tagFilter = new TagFilter(tagData); + tagDatas.put(uuid, tagFilter); + } + } + return tagFilter; + } } diff --git a/app/src/main/res/layout/chip_task_list.xml b/app/src/main/res/layout/chip_task_list.xml new file mode 100644 index 000000000..ffc1296ed --- /dev/null +++ b/app/src/main/res/layout/chip_task_list.xml @@ -0,0 +1,7 @@ + + diff --git a/app/src/main/res/layout/task_adapter_row_body.xml b/app/src/main/res/layout/task_adapter_row_body.xml index 45fe72a2b..240bff5d6 100644 --- a/app/src/main/res/layout/task_adapter_row_body.xml +++ b/app/src/main/res/layout/task_adapter_row_body.xml @@ -74,29 +74,30 @@ android:maxLines="2" android:textColor="@color/text_secondary"/> - + android:layout_gravity="end" + android:clipChildren="false" + android:scrollbarThumbHorizontal="@android:color/transparent"> - - - + android:layout_marginStart="0dp" + + android:layout_marginEnd="@dimen/keyline_first" + android:layout_marginLeft="0dp" + android:layout_marginRight="@dimen/keyline_first" + android:layout_gravity="end" + android:paddingStart="@dimen/keyline_content_inset" + android:paddingLeft="@dimen/keyline_content_inset" + android:clipToPadding="false" + app:singleLine="true"> + + + \ No newline at end of file diff --git a/app/src/main/res/values-w380dp/dimens.xml b/app/src/main/res/values-w380dp/dimens.xml deleted file mode 100644 index 3cf68bf86..000000000 --- a/app/src/main/res/values-w380dp/dimens.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - 16 - \ No newline at end of file diff --git a/app/src/main/res/values-w400dp/dimens.xml b/app/src/main/res/values-w400dp/dimens.xml deleted file mode 100644 index 5096e650e..000000000 --- a/app/src/main/res/values-w400dp/dimens.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - 20 - \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index c807134a0..6b3a32fde 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -12,8 +12,6 @@ 17dp 16sp - 12 - 0.54 0.38 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 14059785a..77fffde65 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -16,6 +16,17 @@ true + +