Convert TaskViewHolder to Kotlin

pull/1004/head
Alex Baker 4 years ago
parent 5a4b5d0ffb
commit 798bcc5c1c

@ -126,7 +126,7 @@ class DragAndDropRecyclerAdapter(
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
taskList.startActionMode()
(viewHolder as TaskViewHolder?)!!.isMoving = true
(viewHolder as TaskViewHolder?)!!.moving = true
dragging = true
val position = viewHolder!!.adapterPosition
updateIndents(viewHolder as TaskViewHolder?, position, position)
@ -155,7 +155,7 @@ class DragAndDropRecyclerAdapter(
return false
}
if (from == -1) {
source.setSelected(false)
source.selected = false
from = fromPosition
}
to = toPosition
@ -201,7 +201,7 @@ class DragAndDropRecyclerAdapter(
if (targetIndent != task.getIndent()) {
if (from == -1) {
taskList.finishActionMode()
vh.setSelected(false)
vh.selected = false
}
}
if (targetIndent < minIndent) {
@ -217,7 +217,7 @@ class DragAndDropRecyclerAdapter(
recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
val vh = viewHolder as TaskViewHolder
vh.isMoving = false
vh.moving = false
dragging = false
drainQueue()
if (taskList.isActionModeActive) {

@ -28,11 +28,11 @@ abstract class TaskListRecyclerAdapter internal constructor(
val task = getItem(position)
if (task != null) {
(holder as TaskViewHolder).bindView(task, taskList.getFilter(), sortMode)
holder.isMoving = false
holder.moving = false
val indent = adapter.getIndent(task)
task.setIndent(indent)
holder.indent = indent
holder.setSelected(adapter.isSelected(task))
holder.selected = adapter.isSelected(task)
}
}
@ -60,7 +60,7 @@ abstract class TaskListRecyclerAdapter internal constructor(
if (!dragAndDropEnabled()) {
taskList.startActionMode()
}
if (taskList.isActionModeActive && !taskViewHolder.isMoving) {
if (taskList.isActionModeActive && !taskViewHolder.moving) {
toggle(taskViewHolder)
}
return true

@ -1,361 +0,0 @@
package org.tasks.tasklist;
import static com.todoroo.andlib.utility.DateUtilities.getRelativeDateTime;
import static com.todoroo.andlib.utility.DateUtilities.getTimeString;
import static org.tasks.date.DateTimeUtils.newDateTime;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Paint;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
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.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.service.TaskCompleter;
import com.todoroo.astrid.ui.CheckableImageView;
import java.util.List;
import java.util.Locale;
import org.tasks.R;
import org.tasks.data.TaskContainer;
import org.tasks.dialogs.Linkify;
import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxProvider;
import org.tasks.ui.ChipProvider;
import java.time.format.FormatStyle;
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final Activity context;
private final Preferences preferences;
private final int textColorSecondary;
private final TaskCompleter taskCompleter;
private final ViewHolderCallbacks callback;
private final DisplayMetrics metrics;
private final int background;
private final int selectedColor;
private final int rowPadding;
private final Linkify linkify;
private final Locale locale;
private final CheckBoxProvider checkBoxProvider;
private final int textColorOverdue;
private final ChipProvider chipProvider;
@BindView(R.id.row)
public ViewGroup row;
@BindView(R.id.due_date)
public TextView dueDate;
public TaskContainer task;
@BindView(R.id.rowBody)
ViewGroup rowBody;
@BindView(R.id.title)
TextView nameView;
@BindView(R.id.description)
TextView description;
@BindView(R.id.completeBox)
CheckableImageView completeBox;
@BindView(R.id.chip_group)
ChipGroup chipGroup;
@BindView(R.id.hidden_icon)
View hiddenIcon;
private int indent;
private boolean selected;
private boolean moving;
private int minIndent;
private int maxIndent;
TaskViewHolder(
Activity context,
ViewGroup view,
Preferences preferences,
int fontSize,
ChipProvider chipProvider,
CheckBoxProvider checkBoxProvider,
int textColorOverdue,
int textColorSecondary,
TaskCompleter taskCompleter,
ViewHolderCallbacks callback,
DisplayMetrics metrics,
int background,
int selectedColor,
int rowPadding,
Linkify linkify,
Locale locale) {
super(view);
this.context = context;
this.preferences = preferences;
this.chipProvider = chipProvider;
this.checkBoxProvider = checkBoxProvider;
this.textColorOverdue = textColorOverdue;
this.textColorSecondary = textColorSecondary;
this.taskCompleter = taskCompleter;
this.callback = callback;
this.metrics = metrics;
this.background = background;
this.selectedColor = selectedColor;
this.rowPadding = rowPadding;
this.linkify = linkify;
this.locale = locale;
ButterKnife.bind(this, view);
if (preferences.getBoolean(R.string.p_fullTaskTitle, false)) {
nameView.setMaxLines(Integer.MAX_VALUE);
nameView.setSingleLine(false);
nameView.setEllipsize(null);
}
if (preferences.getBoolean(R.string.p_show_full_description, false)) {
description.setMaxLines(Integer.MAX_VALUE);
description.setSingleLine(false);
description.setEllipsize(null);
}
setTopPadding(rowPadding, nameView, completeBox, dueDate, hiddenIcon);
setBottomPadding(rowPadding, completeBox, dueDate);
nameView.setTextSize(fontSize);
description.setTextSize(fontSize);
int fontSizeDetails = Math.max(10, fontSize - 2);
dueDate.setTextSize(fontSizeDetails);
view.setTag(this);
for (int i = 0; i < view.getChildCount(); i++) {
view.getChildAt(i).setTag(this);
}
}
private void setTopPadding(int padding, View... views) {
for (View v : views) {
v.setPaddingRelative(v.getPaddingStart(), padding, v.getPaddingEnd(), v.getPaddingBottom());
}
}
private void setBottomPadding(int padding, View... views) {
for (View v : views) {
v.setPaddingRelative(v.getPaddingStart(), v.getPaddingTop(), v.getPaddingEnd(), padding);
}
}
boolean isMoving() {
return moving;
}
void setMoving(boolean moving) {
this.moving = moving;
updateBackground();
}
private void updateBackground() {
if (selected || moving) {
rowBody.setBackgroundColor(selectedColor);
} else {
rowBody.setBackgroundResource(background);
rowBody.getBackground().jumpToCurrentState();
}
}
public void setSelected(boolean selected) {
this.selected = selected;
updateBackground();
}
@SuppressLint("NewApi")
public void setIndent(int indent) {
this.indent = indent;
int indentSize = getIndentSize(indent);
MarginLayoutParams layoutParams = (MarginLayoutParams) row.getLayoutParams();
layoutParams.setMarginStart(indentSize);
row.setLayoutParams(layoutParams);
}
float getShiftSize() {
return 20 * metrics.density;
}
private int getIndentSize(int indent) {
return Math.round(indent * getShiftSize());
}
void bindView(TaskContainer task, Filter filter, int sortMode) {
this.task = task;
this.indent = task.indent;
nameView.setText(task.getTitle());
hiddenIcon.setVisibility(task.isHidden() ? View.VISIBLE : View.GONE);
setupTitleAndCheckbox();
setupDueDate(sortMode);
setupChips(filter);
if (preferences.getBoolean(R.string.p_show_description, true)) {
description.setText(task.getNotes());
description.setVisibility(task.hasNotes() ? View.VISIBLE : View.GONE);
}
if (preferences.getBoolean(R.string.p_linkify_task_list, false)) {
linkify.linkify(nameView, this::onRowBodyClick);
linkify.linkify(description, this::onRowBodyClick);
nameView.setOnLongClickListener(view -> onRowBodyLongClick());
description.setOnLongClickListener(view -> onRowBodyLongClick());
}
if (chipGroup.getVisibility() == View.VISIBLE) {
setBottomPadding(rowPadding, chipGroup);
setBottomPadding(0, description, nameView);
} else if (description.getVisibility() == View.VISIBLE) {
setBottomPadding(rowPadding, description);
setBottomPadding(0, nameView);
} else {
setBottomPadding(rowPadding, nameView);
}
}
private void setupTitleAndCheckbox() {
if (task.isCompleted()) {
nameView.setTextColor(context.getColor(R.color.text_tertiary));
nameView.setPaintFlags(nameView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
nameView.setTextColor(
context.getColor(task.isHidden() ? R.color.text_tertiary : R.color.text_primary));
nameView.setPaintFlags(nameView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
}
completeBox.setChecked(task.isCompleted());
completeBox.setImageDrawable(checkBoxProvider.getCheckBox(task.getTask()));
completeBox.invalidate();
}
private void setupDueDate(int sortMode) {
if (task.hasDueDate()) {
if (task.isOverdue()) {
dueDate.setTextColor(textColorOverdue);
} else {
dueDate.setTextColor(textColorSecondary);
}
String dateValue;
if (sortMode == SortHelper.SORT_DUE
&& task.sortGroup != null
&& !preferences.getBoolean(R.string.p_disable_subtasks, false)) {
dateValue =
task.hasDueTime() ? getTimeString(context, newDateTime(task.getDueDate())) : null;
} else {
dateValue = getRelativeDateTime(context, task.getDueDate(), locale, FormatStyle.MEDIUM);
}
dueDate.setText(dateValue);
dueDate.setVisibility(View.VISIBLE);
} else {
dueDate.setVisibility(View.GONE);
}
}
private void setupChips(Filter filter) {
List<Chip> chips = chipProvider.getChips(filter, indent > 0, task);
if (chips.isEmpty()) {
chipGroup.setVisibility(View.GONE);
} else {
chipGroup.removeAllViews();
for (Chip chip : chips) {
chip.setOnClickListener(this::onChipClick);
chipGroup.addView(chip);
}
chipGroup.setVisibility(View.VISIBLE);
}
}
private void onChipClick(View v) {
Object tag = v.getTag();
if (tag instanceof Filter) {
callback.onClick((Filter) tag);
} else if (tag instanceof TaskContainer) {
TaskContainer task = (TaskContainer) tag;
callback.toggleSubtasks(task, !task.isCollapsed());
}
}
@OnClick(R.id.rowBody)
void onRowBodyClick() {
callback.onClick(this);
}
@OnLongClick(R.id.rowBody)
boolean onRowBodyLongClick() {
return callback.onLongPress(this);
}
@OnClick(R.id.completeBox)
void onCompleteBoxClick() {
if (task == null) {
return;
}
boolean newState = completeBox.isChecked();
if (newState != task.isCompleted()) {
taskCompleter.setComplete(task.getTask(), newState);
callback.onCompletedTask(task, newState);
}
// set check box to actual action item state
setupTitleAndCheckbox();
}
@OnClick(R.id.due_date)
void changeDueDate() {
callback.onChangeDueDate(task);
}
public int getIndent() {
return indent;
}
void setMinIndent(int minIndent) {
this.minIndent = minIndent;
if (task.getTargetIndent() < minIndent) {
task.setTargetIndent(minIndent);
}
}
void setMaxIndent(int maxIndent) {
this.maxIndent = maxIndent;
if (task.getTargetIndent() > maxIndent) {
task.setTargetIndent(maxIndent);
}
}
int getMinIndent() {
return minIndent;
}
int getMaxIndent() {
return maxIndent;
}
public interface ViewHolderCallbacks {
void onCompletedTask(TaskContainer task, boolean newState);
void onClick(TaskViewHolder taskViewHolder);
void onClick(Filter filter);
void toggleSubtasks(TaskContainer task, boolean collapsed);
boolean onLongPress(TaskViewHolder taskViewHolder);
void onChangeDueDate(TaskContainer task);
}
}

@ -0,0 +1,288 @@
package org.tasks.tasklist
import android.app.Activity
import android.graphics.Paint
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
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.ChipGroup
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.SortHelper
import com.todoroo.astrid.service.TaskCompleter
import com.todoroo.astrid.ui.CheckableImageView
import org.tasks.R
import org.tasks.data.TaskContainer
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.dialogs.Linkify
import org.tasks.preferences.Preferences
import org.tasks.ui.CheckBoxProvider
import org.tasks.ui.ChipProvider
import java.time.format.FormatStyle
import java.util.*
import kotlin.math.max
import kotlin.math.roundToInt
class TaskViewHolder internal constructor(
private val context: Activity,
view: ViewGroup,
private val preferences: Preferences,
fontSize: Int,
private val chipProvider: ChipProvider,
private val checkBoxProvider: CheckBoxProvider,
private val textColorOverdue: Int,
private val textColorSecondary: Int,
private val taskCompleter: TaskCompleter,
private val callback: ViewHolderCallbacks,
private val metrics: DisplayMetrics,
private val background: Int,
private val selectedColor: Int,
private val rowPadding: Int,
private val linkify: Linkify,
private val locale: Locale) : RecyclerView.ViewHolder(view) {
@BindView(R.id.row)
lateinit var row: ViewGroup
@BindView(R.id.due_date)
lateinit var dueDate: TextView
@BindView(R.id.rowBody)
lateinit var rowBody: ViewGroup
@BindView(R.id.title)
lateinit var nameView: TextView
@BindView(R.id.description)
lateinit var description: TextView
@BindView(R.id.completeBox)
lateinit var completeBox: CheckableImageView
@BindView(R.id.chip_group)
lateinit var chipGroup: ChipGroup
@BindView(R.id.hidden_icon)
lateinit var hiddenIcon: View
lateinit var task: TaskContainer
var indent = 0
set(value) {
field = value
val indentSize = getIndentSize(value)
val layoutParams = row.layoutParams as MarginLayoutParams
layoutParams.marginStart = indentSize
row.layoutParams = layoutParams
}
var selected = false
set(value) {
field = value
updateBackground()
}
var moving = false
set(value) {
field = value
updateBackground()
}
var minIndent = 0
set(value) {
field = value
if (task.targetIndent < value) {
task.targetIndent = value
}
}
var maxIndent = 0
set(value) {
field = value
if (task.targetIndent > value) {
task.targetIndent = value
}
}
private fun setTopPadding(padding: Int, vararg views: View) {
for (v in views) {
v.setPaddingRelative(v.paddingStart, padding, v.paddingEnd, v.paddingBottom)
}
}
private fun setBottomPadding(padding: Int, vararg views: View) {
for (v in views) {
v.setPaddingRelative(v.paddingStart, v.paddingTop, v.paddingEnd, padding)
}
}
private fun updateBackground() {
if (selected || moving) {
rowBody.setBackgroundColor(selectedColor)
} else {
rowBody.setBackgroundResource(background)
rowBody.background.jumpToCurrentState()
}
}
val shiftSize: Float
get() = 20 * metrics.density
private fun getIndentSize(indent: Int) = (indent * shiftSize).roundToInt()
fun bindView(task: TaskContainer, filter: Filter, sortMode: Int) {
this.task = task
indent = task.indent
nameView.text = task.title
hiddenIcon.visibility = if (task.isHidden) View.VISIBLE else View.GONE
setupTitleAndCheckbox()
setupDueDate(sortMode)
setupChips(filter)
if (preferences.getBoolean(R.string.p_show_description, true)) {
description.text = task.notes
description.visibility = if (task.hasNotes()) View.VISIBLE else View.GONE
}
if (preferences.getBoolean(R.string.p_linkify_task_list, false)) {
linkify.linkify(nameView) { onRowBodyClick() }
linkify.linkify(description) { onRowBodyClick() }
nameView.setOnLongClickListener { onRowBodyLongClick() }
description.setOnLongClickListener { onRowBodyLongClick() }
}
when {
chipGroup.visibility == View.VISIBLE -> {
setBottomPadding(rowPadding, chipGroup)
setBottomPadding(0, description, nameView)
}
description.visibility == View.VISIBLE -> {
setBottomPadding(rowPadding, description)
setBottomPadding(0, nameView)
}
else -> {
setBottomPadding(rowPadding, nameView)
}
}
}
private fun setupTitleAndCheckbox() {
if (task.isCompleted) {
nameView.setTextColor(context.getColor(R.color.text_tertiary))
nameView.paintFlags = nameView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
} else {
nameView.setTextColor(
context.getColor(if (task.isHidden) R.color.text_tertiary else R.color.text_primary))
nameView.paintFlags = nameView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
completeBox.isChecked = task.isCompleted
completeBox.setImageDrawable(checkBoxProvider.getCheckBox(task.getTask()))
completeBox.invalidate()
}
private fun setupDueDate(sortMode: Int) {
if (task.hasDueDate()) {
if (task.isOverdue) {
dueDate.setTextColor(textColorOverdue)
} else {
dueDate.setTextColor(textColorSecondary)
}
val dateValue: String? = if (sortMode == SortHelper.SORT_DUE && task.sortGroup != null && !preferences.getBoolean(R.string.p_disable_subtasks, false)) {
if (task.hasDueTime()) DateUtilities.getTimeString(context, newDateTime(task.dueDate)) else null
} else {
DateUtilities.getRelativeDateTime(context, task.dueDate, locale, FormatStyle.MEDIUM)
}
dueDate.text = dateValue
dueDate.visibility = View.VISIBLE
} else {
dueDate.visibility = View.GONE
}
}
private fun setupChips(filter: Filter) {
val chips = chipProvider.getChips(filter, indent > 0, task)
if (chips.isEmpty()) {
chipGroup.visibility = View.GONE
} else {
chipGroup.removeAllViews()
for (chip in chips) {
chip.setOnClickListener { v: View -> onChipClick(v) }
chipGroup.addView(chip)
}
chipGroup.visibility = View.VISIBLE
}
}
private fun onChipClick(v: View) {
val tag = v.tag
if (tag is Filter) {
callback.onClick(tag)
} else if (tag is TaskContainer) {
callback.toggleSubtasks(tag, !tag.isCollapsed)
}
}
@OnClick(R.id.rowBody)
fun onRowBodyClick() {
callback.onClick(this)
}
@OnLongClick(R.id.rowBody)
fun onRowBodyLongClick(): Boolean {
return callback.onLongPress(this)
}
@OnClick(R.id.completeBox)
fun onCompleteBoxClick() {
val newState = completeBox.isChecked
if (newState != task.isCompleted) {
taskCompleter.setComplete(task.getTask(), newState)
callback.onCompletedTask(task, newState)
}
// set check box to actual action item state
setupTitleAndCheckbox()
}
@OnClick(R.id.due_date)
fun changeDueDate() {
callback.onChangeDueDate(task)
}
interface ViewHolderCallbacks {
fun onCompletedTask(task: TaskContainer, newState: Boolean)
fun onClick(taskViewHolder: TaskViewHolder)
fun onClick(filter: Filter)
fun toggleSubtasks(task: TaskContainer, collapsed: Boolean)
fun onLongPress(taskViewHolder: TaskViewHolder): Boolean
fun onChangeDueDate(task: TaskContainer)
}
init {
ButterKnife.bind(this, view)
if (preferences.getBoolean(R.string.p_fullTaskTitle, false)) {
nameView.maxLines = Int.MAX_VALUE
nameView.isSingleLine = false
nameView.ellipsize = null
}
if (preferences.getBoolean(R.string.p_show_full_description, false)) {
description.maxLines = Int.MAX_VALUE
description.isSingleLine = false
description.ellipsize = null
}
setTopPadding(rowPadding, nameView, completeBox, dueDate, hiddenIcon)
setBottomPadding(rowPadding, completeBox, dueDate)
nameView.textSize = fontSize.toFloat()
description.textSize = fontSize.toFloat()
val fontSizeDetails = max(10, fontSize - 2)
dueDate.textSize = fontSizeDetails.toFloat()
view.tag = this
for (i in 0 until view.childCount) {
view.getChildAt(i).tag = this
}
}
}

@ -41,7 +41,7 @@ class ViewHolderFactory @Inject constructor(
LayoutInflater.from(context).inflate(R.layout.task_adapter_header, parent, false),
callback)
fun newViewHolder(parent: ViewGroup?, callbacks: ViewHolderCallbacks?) =
fun newViewHolder(parent: ViewGroup?, callbacks: ViewHolderCallbacks) =
TaskViewHolder(
context as Activity,
LayoutInflater.from(context).inflate(R.layout.task_adapter_row, parent, false) as ViewGroup,

Loading…
Cancel
Save