Loading opentasks/src/main/java/org/dmfs/tasks/groupings/BaseTaskViewDescriptor.java +104 −6 Original line number Original line Diff line number Diff line Loading @@ -17,9 +17,15 @@ package org.dmfs.tasks.groupings; package org.dmfs.tasks.groupings; import android.annotation.SuppressLint; import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.TextUtils; import android.text.format.Time; import android.text.format.Time; import android.text.style.DynamicDrawableSpan; import android.text.style.ImageSpan; import android.view.View; import android.view.View; import android.widget.ImageView; import android.widget.ImageView; import android.widget.TextView; import android.widget.TextView; Loading @@ -27,16 +33,26 @@ import android.widget.TextView; import com.google.android.material.card.MaterialCardView; import com.google.android.material.card.MaterialCardView; import org.dmfs.android.bolts.color.colors.AttributeColor; import org.dmfs.android.bolts.color.colors.AttributeColor; import org.dmfs.iterables.decorators.Sieved; import org.dmfs.jems.single.elementary.Reduced; import org.dmfs.tasks.R; import org.dmfs.tasks.R; import org.dmfs.tasks.model.DescriptionItem; import org.dmfs.tasks.model.TaskFieldAdapters; import org.dmfs.tasks.model.TaskFieldAdapters; import org.dmfs.tasks.utils.DateFormatter; import org.dmfs.tasks.utils.DateFormatter; import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; import org.dmfs.tasks.utils.ViewDescriptor; import org.dmfs.tasks.utils.ViewDescriptor; import java.util.List; import java.util.TimeZone; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.collection.SparseArrayCompat; import androidx.collection.SparseArrayCompat; import androidx.core.content.ContextCompat; import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE; import static org.dmfs.tasks.contract.TaskContract.TaskColumns.STATUS_CANCELLED; import static org.dmfs.tasks.contract.TaskContract.TaskColumns.STATUS_CANCELLED; import static org.dmfs.tasks.model.TaskFieldAdapters.IS_CLOSED; import static org.dmfs.tasks.model.TaskFieldAdapters.IS_CLOSED; Loading @@ -51,6 +67,8 @@ import static org.dmfs.tasks.model.TaskFieldAdapters.STATUS; public abstract class BaseTaskViewDescriptor implements ViewDescriptor public abstract class BaseTaskViewDescriptor implements ViewDescriptor { { private final static Pattern CHECKED_PATTERN = Pattern.compile("(-\\s*)?\\[[xX]]"); private final static Pattern UNCHECKED_PATTERN = Pattern.compile("(-\\s*)?\\[\\s?]"); /** /** * We use this to get the current time. * We use this to get the current time. */ */ Loading Loading @@ -119,22 +137,102 @@ public abstract class BaseTaskViewDescriptor implements ViewDescriptor protected void setDescription(View view, Cursor cursor) protected void setDescription(View view, Cursor cursor) { { String description = TaskFieldAdapters.DESCRIPTION.get(cursor); TextView descriptionView = getView(view, android.R.id.text1); View content = getView(view, R.id.cardcontent); View content = getView(view, R.id.cardcontent); if (TextUtils.isEmpty(description) || TRUE.equals(IS_CLOSED.get(cursor))) TextView descriptionView = getView(content, android.R.id.text1); if (TextUtils.isEmpty(TaskFieldAdapters.DESCRIPTION.get(cursor)) || TRUE.equals(IS_CLOSED.get(cursor))) { { content.setVisibility(View.GONE); content.setVisibility(View.GONE); } } else else { { content.setVisibility(View.VISIBLE); content.setVisibility(View.VISIBLE); List<DescriptionItem> checkList = TaskFieldAdapters.DESCRIPTION_CHECKLIST.get(cursor); if (checkList.size() > 0 && !checkList.get(0).checkbox) { String description = checkList.get(0).text; if (description.length() > 150) if (description.length() > 150) { { description = description.substring(0, 150); description = description.substring(0, 150) + "…"; } } descriptionView.setVisibility(View.VISIBLE); descriptionView.setText(description); descriptionView.setText(description); } } else { descriptionView.setVisibility(View.GONE); } TextView checkboxItemCountView = getView(content, R.id.checkbox_item_count); Iterable<DescriptionItem> checkedItems = new Sieved<>(item -> item.checkbox, checkList); int checkboxItemCount = new Reduced<DescriptionItem, Integer>(() -> 0, (count, ignored) -> count + 1, checkedItems).value(); if (checkboxItemCount == 0) { checkboxItemCountView.setVisibility(View.GONE); } else { checkboxItemCountView.setVisibility(View.VISIBLE); int checked = new Reduced<DescriptionItem, Integer>(() -> 0, (count, ignored) -> count + 1, new Sieved<>(item -> item.checked, checkedItems)).value(); if (checked == 0) { checkboxItemCountView.setText( withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_none_checked, checkboxItemCount))); } else if (checked == checkboxItemCount) { checkboxItemCountView.setText( withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_all_checked, checkboxItemCount))); } else { checkboxItemCountView.setText(withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_partially_checked, checkboxItemCount - checked, checked))); } } } } private Spannable withCheckBoxes( @NonNull TextView view, @NonNull String s) { return withDrawable( view, withDrawable( view, new SpannableString(s), CHECKED_PATTERN, R.drawable.ic_outline_check_box_24), UNCHECKED_PATTERN, R.drawable.ic_outline_check_box_outline_blank_24); } private Spannable withDrawable( @NonNull TextView view, @NonNull Spannable s, @NonNull Pattern pattern, @DrawableRes int drawable) { Context context = view.getContext(); Matcher matcher = pattern.matcher(s.toString()); while (matcher.find()) { Drawable drawable1 = ContextCompat.getDrawable(context, drawable); int lineHeight = view.getLineHeight(); int additionalSpace = (int) ((lineHeight - view.getTextSize()) / 2); drawable1.setBounds(0, 0, lineHeight + additionalSpace, lineHeight + additionalSpace); drawable1.setTint(0xc0ffffff); s.setSpan(new ImageSpan(drawable1, DynamicDrawableSpan.ALIGN_BOTTOM), matcher.start(), matcher.end(), SPAN_EXCLUSIVE_EXCLUSIVE); } return s; } } Loading opentasks/src/main/res/drawable/ic_outline_check_box_24.xml 0 → 100644 +10 −0 Original line number Original line Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,5h14v14zM17.99,9l-1.41,-1.42 -6.59,6.59 -2.58,-2.57 -1.42,1.41 4,3.99z" /> </vector> opentasks/src/main/res/drawable/ic_outline_check_box_outline_blank_24.xml 0 → 100644 +10 −0 Original line number Original line Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" /> </vector> opentasks/src/main/res/layout/task_list_element.xml +22 −8 Original line number Original line Diff line number Diff line Loading @@ -181,15 +181,15 @@ android:layout_marginTop="@dimen/opentasks_cardlist_title_height" android:layout_marginTop="@dimen/opentasks_cardlist_title_height" android:duplicateParentState="true"> android:duplicateParentState="true"> <FrameLayout <RelativeLayout android:id="@+id/cardcontent" android:id="@+id/cardcontent" style="@style/tasks_expandable_list_item" style="@style/tasks_expandable_list_item" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:duplicateParentState="true" android:duplicateParentState="true" android:paddingLeft="8dp" android:paddingLeft="12dp" android:paddingRight="8dp" android:paddingRight="12dp" android:paddingBottom="8dp" android:paddingBottom="12dp" android:visibility="visible"> android:visibility="visible"> <TextView <TextView Loading @@ -200,20 +200,34 @@ android:gravity="center_vertical" android:gravity="center_vertical" android:maxLines="2" android:maxLines="2" android:orientation="vertical" android:orientation="vertical" android:padding="8dip" android:padding="4dip" android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="#c0ffffff" android:textColor="#c0ffffff" android:textIsSelectable="false" android:textIsSelectable="false" android:textSize="12sp" /> android:textSize="12sp" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/checkbox_item_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@android:id/text1" android:layout_alignParentStart="true" android:padding="4dp" android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="#c0ffffff" android:textIsSelectable="false" android:textSize="12sp" android:visibility="gone" /> </RelativeLayout> </FrameLayout> </FrameLayout> </FrameLayout> <FrameLayout <FrameLayout android:id="@+id/priority_label" android:id="@+id/priority_label" android:layout_width="6dip" android:layout_width="6dip" android:layout_height="match_parent" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_alignParentRight="true" android:layout_gravity="right" android:layout_centerVertical="true" android:layout_centerVertical="true" android:layout_gravity="right" android:minHeight="@dimen/opentasks_cardlist_title_height"> android:minHeight="@dimen/opentasks_cardlist_title_height"> <View <View Loading opentasks/src/main/res/values/strings.xml +4 −0 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,10 @@ <string name="quick_add_task_created">Task created</string> <string name="quick_add_task_created">Task created</string> <string name="initial_local_task_list_name">My tasks</string> <string name="initial_local_task_list_name">My tasks</string> <string name="opentasks_checkbox_item_count_none_checked">[ ] %d</string> <string name="opentasks_checkbox_item_count_all_checked">[x] %d</string> <string name="opentasks_checkbox_item_count_partially_checked">[ ] %d / [x] %d</string> <plurals name="number_of_tasks"> <plurals name="number_of_tasks"> <item quantity="one">%d task</item> <item quantity="one">%d task</item> <item quantity="other">%d tasks</item> <item quantity="other">%d tasks</item> Loading Loading
opentasks/src/main/java/org/dmfs/tasks/groupings/BaseTaskViewDescriptor.java +104 −6 Original line number Original line Diff line number Diff line Loading @@ -17,9 +17,15 @@ package org.dmfs.tasks.groupings; package org.dmfs.tasks.groupings; import android.annotation.SuppressLint; import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.TextUtils; import android.text.format.Time; import android.text.format.Time; import android.text.style.DynamicDrawableSpan; import android.text.style.ImageSpan; import android.view.View; import android.view.View; import android.widget.ImageView; import android.widget.ImageView; import android.widget.TextView; import android.widget.TextView; Loading @@ -27,16 +33,26 @@ import android.widget.TextView; import com.google.android.material.card.MaterialCardView; import com.google.android.material.card.MaterialCardView; import org.dmfs.android.bolts.color.colors.AttributeColor; import org.dmfs.android.bolts.color.colors.AttributeColor; import org.dmfs.iterables.decorators.Sieved; import org.dmfs.jems.single.elementary.Reduced; import org.dmfs.tasks.R; import org.dmfs.tasks.R; import org.dmfs.tasks.model.DescriptionItem; import org.dmfs.tasks.model.TaskFieldAdapters; import org.dmfs.tasks.model.TaskFieldAdapters; import org.dmfs.tasks.utils.DateFormatter; import org.dmfs.tasks.utils.DateFormatter; import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; import org.dmfs.tasks.utils.ViewDescriptor; import org.dmfs.tasks.utils.ViewDescriptor; import java.util.List; import java.util.TimeZone; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.collection.SparseArrayCompat; import androidx.collection.SparseArrayCompat; import androidx.core.content.ContextCompat; import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE; import static org.dmfs.tasks.contract.TaskContract.TaskColumns.STATUS_CANCELLED; import static org.dmfs.tasks.contract.TaskContract.TaskColumns.STATUS_CANCELLED; import static org.dmfs.tasks.model.TaskFieldAdapters.IS_CLOSED; import static org.dmfs.tasks.model.TaskFieldAdapters.IS_CLOSED; Loading @@ -51,6 +67,8 @@ import static org.dmfs.tasks.model.TaskFieldAdapters.STATUS; public abstract class BaseTaskViewDescriptor implements ViewDescriptor public abstract class BaseTaskViewDescriptor implements ViewDescriptor { { private final static Pattern CHECKED_PATTERN = Pattern.compile("(-\\s*)?\\[[xX]]"); private final static Pattern UNCHECKED_PATTERN = Pattern.compile("(-\\s*)?\\[\\s?]"); /** /** * We use this to get the current time. * We use this to get the current time. */ */ Loading Loading @@ -119,22 +137,102 @@ public abstract class BaseTaskViewDescriptor implements ViewDescriptor protected void setDescription(View view, Cursor cursor) protected void setDescription(View view, Cursor cursor) { { String description = TaskFieldAdapters.DESCRIPTION.get(cursor); TextView descriptionView = getView(view, android.R.id.text1); View content = getView(view, R.id.cardcontent); View content = getView(view, R.id.cardcontent); if (TextUtils.isEmpty(description) || TRUE.equals(IS_CLOSED.get(cursor))) TextView descriptionView = getView(content, android.R.id.text1); if (TextUtils.isEmpty(TaskFieldAdapters.DESCRIPTION.get(cursor)) || TRUE.equals(IS_CLOSED.get(cursor))) { { content.setVisibility(View.GONE); content.setVisibility(View.GONE); } } else else { { content.setVisibility(View.VISIBLE); content.setVisibility(View.VISIBLE); List<DescriptionItem> checkList = TaskFieldAdapters.DESCRIPTION_CHECKLIST.get(cursor); if (checkList.size() > 0 && !checkList.get(0).checkbox) { String description = checkList.get(0).text; if (description.length() > 150) if (description.length() > 150) { { description = description.substring(0, 150); description = description.substring(0, 150) + "…"; } } descriptionView.setVisibility(View.VISIBLE); descriptionView.setText(description); descriptionView.setText(description); } } else { descriptionView.setVisibility(View.GONE); } TextView checkboxItemCountView = getView(content, R.id.checkbox_item_count); Iterable<DescriptionItem> checkedItems = new Sieved<>(item -> item.checkbox, checkList); int checkboxItemCount = new Reduced<DescriptionItem, Integer>(() -> 0, (count, ignored) -> count + 1, checkedItems).value(); if (checkboxItemCount == 0) { checkboxItemCountView.setVisibility(View.GONE); } else { checkboxItemCountView.setVisibility(View.VISIBLE); int checked = new Reduced<DescriptionItem, Integer>(() -> 0, (count, ignored) -> count + 1, new Sieved<>(item -> item.checked, checkedItems)).value(); if (checked == 0) { checkboxItemCountView.setText( withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_none_checked, checkboxItemCount))); } else if (checked == checkboxItemCount) { checkboxItemCountView.setText( withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_all_checked, checkboxItemCount))); } else { checkboxItemCountView.setText(withCheckBoxes(checkboxItemCountView, view.getContext().getString(R.string.opentasks_checkbox_item_count_partially_checked, checkboxItemCount - checked, checked))); } } } } private Spannable withCheckBoxes( @NonNull TextView view, @NonNull String s) { return withDrawable( view, withDrawable( view, new SpannableString(s), CHECKED_PATTERN, R.drawable.ic_outline_check_box_24), UNCHECKED_PATTERN, R.drawable.ic_outline_check_box_outline_blank_24); } private Spannable withDrawable( @NonNull TextView view, @NonNull Spannable s, @NonNull Pattern pattern, @DrawableRes int drawable) { Context context = view.getContext(); Matcher matcher = pattern.matcher(s.toString()); while (matcher.find()) { Drawable drawable1 = ContextCompat.getDrawable(context, drawable); int lineHeight = view.getLineHeight(); int additionalSpace = (int) ((lineHeight - view.getTextSize()) / 2); drawable1.setBounds(0, 0, lineHeight + additionalSpace, lineHeight + additionalSpace); drawable1.setTint(0xc0ffffff); s.setSpan(new ImageSpan(drawable1, DynamicDrawableSpan.ALIGN_BOTTOM), matcher.start(), matcher.end(), SPAN_EXCLUSIVE_EXCLUSIVE); } return s; } } Loading
opentasks/src/main/res/drawable/ic_outline_check_box_24.xml 0 → 100644 +10 −0 Original line number Original line Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,5h14v14zM17.99,9l-1.41,-1.42 -6.59,6.59 -2.58,-2.57 -1.42,1.41 4,3.99z" /> </vector>
opentasks/src/main/res/drawable/ic_outline_check_box_outline_blank_24.xml 0 → 100644 +10 −0 Original line number Original line Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" /> </vector>
opentasks/src/main/res/layout/task_list_element.xml +22 −8 Original line number Original line Diff line number Diff line Loading @@ -181,15 +181,15 @@ android:layout_marginTop="@dimen/opentasks_cardlist_title_height" android:layout_marginTop="@dimen/opentasks_cardlist_title_height" android:duplicateParentState="true"> android:duplicateParentState="true"> <FrameLayout <RelativeLayout android:id="@+id/cardcontent" android:id="@+id/cardcontent" style="@style/tasks_expandable_list_item" style="@style/tasks_expandable_list_item" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:duplicateParentState="true" android:duplicateParentState="true" android:paddingLeft="8dp" android:paddingLeft="12dp" android:paddingRight="8dp" android:paddingRight="12dp" android:paddingBottom="8dp" android:paddingBottom="12dp" android:visibility="visible"> android:visibility="visible"> <TextView <TextView Loading @@ -200,20 +200,34 @@ android:gravity="center_vertical" android:gravity="center_vertical" android:maxLines="2" android:maxLines="2" android:orientation="vertical" android:orientation="vertical" android:padding="8dip" android:padding="4dip" android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="#c0ffffff" android:textColor="#c0ffffff" android:textIsSelectable="false" android:textIsSelectable="false" android:textSize="12sp" /> android:textSize="12sp" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/checkbox_item_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@android:id/text1" android:layout_alignParentStart="true" android:padding="4dp" android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="#c0ffffff" android:textIsSelectable="false" android:textSize="12sp" android:visibility="gone" /> </RelativeLayout> </FrameLayout> </FrameLayout> </FrameLayout> <FrameLayout <FrameLayout android:id="@+id/priority_label" android:id="@+id/priority_label" android:layout_width="6dip" android:layout_width="6dip" android:layout_height="match_parent" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_alignParentRight="true" android:layout_gravity="right" android:layout_centerVertical="true" android:layout_centerVertical="true" android:layout_gravity="right" android:minHeight="@dimen/opentasks_cardlist_title_height"> android:minHeight="@dimen/opentasks_cardlist_title_height"> <View <View Loading
opentasks/src/main/res/values/strings.xml +4 −0 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,10 @@ <string name="quick_add_task_created">Task created</string> <string name="quick_add_task_created">Task created</string> <string name="initial_local_task_list_name">My tasks</string> <string name="initial_local_task_list_name">My tasks</string> <string name="opentasks_checkbox_item_count_none_checked">[ ] %d</string> <string name="opentasks_checkbox_item_count_all_checked">[x] %d</string> <string name="opentasks_checkbox_item_count_partially_checked">[ ] %d / [x] %d</string> <plurals name="number_of_tasks"> <plurals name="number_of_tasks"> <item quantity="one">%d task</item> <item quantity="one">%d task</item> <item quantity="other">%d tasks</item> <item quantity="other">%d tasks</item> Loading