Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit c2cf2f2e authored by Marten Gajda's avatar Marten Gajda Committed by GitHub
Browse files

Show checked item count in task list (#973)

parent b3cf126b
Loading
Loading
Loading
Loading
+104 −6
Original line number Original line Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.
     */
     */
@@ -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;
    }
    }




+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>
+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>
+22 −8
Original line number Original line Diff line number Diff line
@@ -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
@@ -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
+4 −0
Original line number Original line Diff line number Diff line
@@ -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>