Refactor widget

pull/2807/head
Alex Baker 2 months ago
parent 4d1d6a06a8
commit f33cc896dd

@ -315,7 +315,7 @@
<!-- ======================================================== Services = -->
<service
android:name=".widget.ScrollableWidgetUpdateService"
android:name=".widget.TasksWidgetAdapter"
android:permission="android.permission.BIND_REMOTEVIEWS"/>
<service

@ -79,6 +79,10 @@ class LocalBroadcastManager @Inject constructor(
localBroadcastManager.sendBroadcast(Intent(REFRESH_PURCHASES))
}
fun reconfigureWidgets() {
appWidgetManager.widgetIds.forEach { reconfigureWidget(it) }
}
fun reconfigureWidget(appWidgetId: Int) {
appWidgetManager.reconfigureWidgets(appWidgetId)
}

@ -115,6 +115,12 @@ class Tasks : Application(), Configuration.Provider {
.setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.DEBUG else Log.INFO)
.build()
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
super.onConfigurationChanged(newConfig)
localBroadcastManager.reconfigureWidgets()
}
private class RefreshBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
JobIntentService.enqueueWork(

@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import org.tasks.dialogs.SortSettingsActivity.Companion.WIDGET_NONE
import org.tasks.preferences.Preferences
import org.tasks.tasklist.SectionedDataSource.Companion.HEADER_COMPLETED
import org.tasks.widget.WidgetPreferences
import javax.inject.Inject
@ -82,9 +83,15 @@ class SortSettingsViewModel @Inject constructor(
}
fun setGroupMode(groupMode: Int) {
if (preferences.groupMode == groupMode) {
return
}
if (groupMode != SortHelper.GROUP_NONE) {
preferences.isManualSort = false
preferences.isAstridSort = false
if (preferences is WidgetPreferences) {
preferences.collapsed = setOf(HEADER_COMPLETED)
}
}
preferences.groupMode = groupMode
val ascending = when (groupMode) {

@ -42,10 +42,6 @@ object Context {
val Context.isNightMode: Boolean
get() = nightMode == Configuration.UI_MODE_NIGHT_YES
@Deprecated("Not supposed to use this")
val Context.isSinglePaneLayout: Boolean
get() = !resources.getBoolean(R.bool.two_pane_layout)
fun Context.openUri(resId: Int, vararg formatArgs: Any) = openUri(getString(resId, formatArgs))
fun Context.openUri(url: String?) =

@ -0,0 +1,37 @@
package org.tasks.extensions
import android.graphics.Paint.ANTI_ALIAS_FLAG
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.widget.RemoteViews
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.core.graphics.ColorUtils
import org.tasks.R
fun RemoteViews.setColorFilter(viewId: Int, @ColorInt color: Int) =
setInt(viewId, "setColorFilter", color)
fun RemoteViews.setBackgroundColor(viewId: Int, color: Int, opacity: Int) =
setInt(viewId, "setBackgroundColor", ColorUtils.setAlphaComponent(color, opacity))
fun RemoteViews.setBackgroundResource(viewId: Int, @DrawableRes resId: Int) =
setInt(viewId, "setBackgroundResource", resId)
fun RemoteViews.setTextSize(viewId: Int, size: Float) =
setFloat(viewId, "setTextSize", size)
fun RemoteViews.strikethrough(viewId: Int, strikethrough: Boolean) =
setInt(
viewId,
"setPaintFlags",
if (strikethrough) STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG else ANTI_ALIAS_FLAG
)
fun RemoteViews.setMaxLines(viewId: Int, maxLines: Int) =
setInt(viewId, "setMaxLines", maxLines)
fun RemoteViews.setRipple(viewId: Int, dark: Boolean) =
setBackgroundResource(
viewId,
if (dark) R.drawable.widget_ripple_circle_light else R.drawable.widget_ripple_circle_dark
)

@ -25,7 +25,7 @@ interface QueryPreferences {
val showCompleted: Boolean
var alwaysDisplayFullDate: Boolean
val alwaysDisplayFullDate: Boolean
var completedTasksAtBottom: Boolean
}

@ -39,7 +39,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class ScrollableWidget : InjectingPreferenceFragment() {
class WidgetSettings : InjectingPreferenceFragment() {
companion object {
private const val REQUEST_THEME_SELECTION = 1006
@ -50,8 +50,8 @@ class ScrollableWidget : InjectingPreferenceFragment() {
private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"
private const val FRAG_TAG_FILTER_PICKER = "frag_tag_filter_picker"
fun newScrollableWidget(appWidgetId: Int): ScrollableWidget {
val widget = ScrollableWidget()
fun newWidgetSettings(appWidgetId: Int): WidgetSettings {
val widget = WidgetSettings()
val args = Bundle()
args.putInt(EXTRA_WIDGET_ID, appWidgetId)
widget.arguments = args
@ -116,8 +116,6 @@ class ScrollableWidget : InjectingPreferenceFragment() {
setupCheckbox(R.string.p_widget_show_full_description, false).dependency = showDescription.key
setupList(R.string.p_widget_spacing)
setupList(R.string.p_widget_header_spacing)
setupList(R.string.p_widget_footer_click)
setupList(R.string.p_widget_due_date_click)
setupList(R.string.p_widget_due_date_position, widgetPreferences.dueDatePosition.toString())
val showHeader = setupCheckbox(R.string.p_widget_show_header)
val showTitle = setupCheckbox(R.string.p_widget_show_title)
@ -185,9 +183,8 @@ class ScrollableWidget : InjectingPreferenceFragment() {
updateTheme()
}
REQUEST_COLOR_SELECTION -> if (resultCode == Activity.RESULT_OK) {
widgetPreferences.color = data!!.getIntExtra(
ColorWheelPicker.EXTRA_SELECTED,
0
widgetPreferences.setColor(
data!!.getIntExtra(ColorWheelPicker.EXTRA_SELECTED, 0)
)
updateColor()
}

@ -8,12 +8,12 @@ import org.tasks.data.TaskContainer
import org.tasks.time.DateTimeUtils.startOfDay
class SectionedDataSource(
tasks: List<TaskContainer>,
disableHeaders: Boolean,
val groupMode: Int,
val subtaskMode: Int,
private val collapsed: Set<Long>,
private val completedAtBottom: Boolean,
tasks: List<TaskContainer> = emptyList(),
disableHeaders: Boolean = false,
val groupMode: Int = SortHelper.GROUP_NONE,
val subtaskMode: Int = SortHelper.SORT_MANUAL,
private val collapsed: Set<Long> = emptySet(),
private val completedAtBottom: Boolean = true,
) {
private val tasks = tasks.toMutableList()

@ -71,7 +71,7 @@ class ColorProvider @Inject constructor(
-5792882 to -5135210 // birch
)
fun priorityColor(priority: Int, isDarkMode: Boolean, desaturate: Boolean): Int {
fun priorityColor(priority: Int, isDarkMode: Boolean = false, desaturate: Boolean = false): Int {
val color = when (priority) {
in Int.MIN_VALUE..Task.Priority.HIGH -> RED_500
Task.Priority.MEDIUM -> AMBER_500

@ -248,6 +248,7 @@ public class ThemeColor implements Pickable {
return colorPrimary;
}
@ColorInt
public int getColorOnPrimary() {
return colorOnPrimary;
}

@ -1,15 +1,13 @@
package org.tasks.ui
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import com.todoroo.astrid.data.Task
import dagger.hilt.android.qualifiers.ActivityContext
import org.tasks.R
import org.tasks.themes.ColorProvider
import org.tasks.themes.DrawableUtil
import javax.inject.Inject
class CheckBoxProvider @Inject constructor(
@ -18,29 +16,13 @@ class CheckBoxProvider @Inject constructor(
) {
fun getCheckBox(task: Task) = getDrawable(task.getCheckboxRes(), task.priority)
fun getWidgetCheckBox(task: Task): Bitmap {
val wrapped =
DrawableUtil.getWrapped(context, task.getCheckboxRes())
DrawableUtil.setTint(wrapped, colorProvider.getPriorityColor(task.priority, false))
return convertToBitmap(wrapped)
}
private fun getDrawable(@DrawableRes resId: Int, priority: Int): Drawable {
val original = context.getDrawable(resId)
val original = AppCompatResources.getDrawable(context, resId)
val wrapped = original!!.mutate()
wrapped.setTint(colorProvider.getPriorityColor(priority))
return wrapped
}
private fun convertToBitmap(d: Drawable): Bitmap {
val bitmap =
Bitmap.createBitmap(d.intrinsicWidth, d.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
d.setBounds(0, 0, canvas.width, canvas.height)
d.draw(canvas)
return bitmap
}
companion object {
fun Task.getCheckboxRes() = when {
isCompleted -> R.drawable.ic_outline_check_box_24px

@ -1,395 +0,0 @@
package org.tasks.widget
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Paint
import android.view.View
import android.widget.RemoteViews
import android.widget.RemoteViewsService.RemoteViewsFactory
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.SortHelper
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.subtasks.SubtasksHelper
import kotlinx.coroutines.runBlocking
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.data.TaskContainer
import org.tasks.data.TaskDao
import org.tasks.data.TaskListQuery.getQuery
import org.tasks.date.DateTimeUtils
import org.tasks.extensions.Context.isNightMode
import org.tasks.markdown.Markdown
import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences
import org.tasks.tasklist.HeaderFormatter
import org.tasks.tasklist.SectionedDataSource
import org.tasks.tasklist.SectionedDataSource.Companion.HEADER_COMPLETED
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.ui.CheckBoxProvider
import timber.log.Timber
import java.time.format.FormatStyle
import java.util.Locale
import kotlin.math.max
internal class ScrollableViewsFactory(
private val subtasksHelper: SubtasksHelper,
preferences: Preferences,
private val context: Context,
private val widgetId: Int,
private val taskDao: TaskDao,
private val defaultFilterProvider: DefaultFilterProvider,
private val checkBoxProvider: CheckBoxProvider,
private val locale: Locale,
private val chipProvider: ChipProvider,
private val localBroadcastManager: LocalBroadcastManager,
private val markdown: Markdown,
private val headerFormatter: HeaderFormatter,
) : RemoteViewsFactory {
private val indentPadding: Int
private var showDueDates = false
private var endDueDate = false
private var showCheckboxes = false
private var textSize = 0f
private var dueDateTextSize = 0f
private var filter: Filter? = null
private var textColorPrimary = 0
private var textColorSecondary = 0
private var showFullTaskTitle = false
private var showDescription = false
private var showFullDescription = false
private var vPad = 0
private var hPad = 0
private var handleDueDateClick = false
private var showDividers = false
private var disableGroups = false
private var showSubtasks = false
private var showStartDates = false
private var showPlaces = false
private var showLists = false
private var showTags = false
private var collapsed = mutableSetOf(HEADER_COMPLETED)
private var groupMode = -1
private var subtaskMode = -1
private var tasks = SectionedDataSource(
emptyList(),
disableHeaders = false,
groupMode = SortHelper.GROUP_NONE,
subtaskMode = SortHelper.SORT_MANUAL,
collapsed,
preferences.completedTasksAtBottom,
)
private val widgetPreferences = WidgetPreferences(context, preferences, widgetId)
private var isDark = checkIfDark
private var showFullDate = false
private var compact = false
private val checkIfDark: Boolean
get() = when (widgetPreferences.themeIndex) {
0 -> false
3 -> context.isNightMode
else -> true
}
override fun onCreate() {}
override fun onDataSetChanged() {
runBlocking {
updateSettings()
tasks = SectionedDataSource(
taskDao.fetchTasks { getQuery(filter) },
disableGroups,
groupMode,
subtaskMode,
collapsed,
widgetPreferences.completedTasksAtBottom,
)
if (collapsed.retainAll(tasks.getSectionValues())) {
widgetPreferences.collapsed = collapsed
}
}
}
override fun onDestroy() {}
override fun getCount(): Int {
if (isDark != checkIfDark) {
isDark = !isDark
localBroadcastManager.reconfigureWidget(widgetId)
}
return tasks.size
}
override fun getViewAt(position: Int): RemoteViews? =
if (tasks.isHeader(position)) buildHeader(position) else buildUpdate(position)
override fun getLoadingView(): RemoteViews = newRemoteView()
override fun getViewTypeCount(): Int = 2
override fun getItemId(position: Int) = getTask(position)?.id ?: 0
override fun hasStableIds(): Boolean = true
private fun getCheckbox(task: Task): Bitmap = checkBoxProvider.getWidgetCheckBox(task)
private fun newRemoteView(): RemoteViews = RemoteViews(
BuildConfig.APPLICATION_ID,
if (isDark) R.layout.widget_row_dark else R.layout.widget_row_light
)
private fun buildHeader(position: Int): RemoteViews {
val row = RemoteViews(
BuildConfig.APPLICATION_ID,
if (isDark) R.layout.widget_header_dark else R.layout.widget_header_light
)
val section = tasks.getSection(position)
val sortGroup = section.value
val header: String? = if (filter?.supportsSorting() == true) {
headerFormatter.headerStringBlocking(
value = section.value,
groupMode = groupMode,
alwaysDisplayFullDate = showFullDate,
style = FormatStyle.MEDIUM,
compact = compact,
)
} else {
null
}
row.setTextViewText(R.id.header, header)
row.setImageViewResource(R.id.arrow, if (section.collapsed) {
R.drawable.ic_keyboard_arrow_down_black_18dp
} else {
R.drawable.ic_keyboard_arrow_up_black_18dp
})
row.setTextColor(
R.id.header,
section.headerColor(
context,
groupMode,
if (isDark) R.color.white_60 else R.color.black_60
)
)
if (!showDividers) {
row.setViewVisibility(R.id.divider, View.GONE)
}
row.setOnClickFillInIntent(
R.id.row,
Intent(WidgetClickActivity.TOGGLE_GROUP)
.putExtra(WidgetClickActivity.EXTRA_WIDGET, widgetId)
.putExtra(WidgetClickActivity.EXTRA_GROUP, sortGroup)
.putExtra(WidgetClickActivity.EXTRA_COLLAPSED, !section.collapsed)
)
return row
}
private fun buildUpdate(position: Int): RemoteViews? {
try {
val taskContainer = getTask(position) ?: return null
val task = taskContainer.task
var textColorTitle = textColorPrimary
val row = newRemoteView()
if (task.isHidden) {
textColorTitle = textColorSecondary
}
if (task.isCompleted) {
textColorTitle = textColorSecondary
row.setInt(
R.id.widget_text, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG or Paint.ANTI_ALIAS_FLAG)
} else {
row.setInt(R.id.widget_text, "setPaintFlags", Paint.ANTI_ALIAS_FLAG)
}
row.setFloat(R.id.widget_text, "setTextSize", textSize)
if (showDueDates) {
formatDueDate(row, taskContainer)
} else {
row.setViewVisibility(R.id.widget_due_bottom, View.GONE)
row.setViewVisibility(R.id.widget_due_end, View.GONE)
if (task.hasDueDate() && task.isOverdue) {
textColorTitle = context.getColor(R.color.overdue)
}
}
if (showFullTaskTitle) {
row.setInt(R.id.widget_text, "setMaxLines", Int.MAX_VALUE)
}
row.setTextViewText(
R.id.widget_text,
markdown.toMarkdown(task.title)
)
row.setTextColor(R.id.widget_text, textColorTitle)
if (showDescription && task.hasNotes()) {
row.setFloat(R.id.widget_description, "setTextSize", textSize)
row.setTextViewText(
R.id.widget_description,
markdown.toMarkdown(task.notes)
)
row.setViewVisibility(R.id.widget_description, View.VISIBLE)
if (showFullDescription) {
row.setInt(R.id.widget_description, "setMaxLines", Int.MAX_VALUE)
}
} else {
row.setViewVisibility(R.id.widget_description, View.GONE)
}
row.setOnClickFillInIntent(
R.id.widget_row,
Intent(WidgetClickActivity.EDIT_TASK)
.putExtra(WidgetClickActivity.EXTRA_FILTER, filter)
.putExtra(WidgetClickActivity.EXTRA_TASK, task))
if (showCheckboxes) {
row.setViewPadding(R.id.widget_complete_box, hPad, vPad, hPad, vPad)
row.setImageViewBitmap(R.id.widget_complete_box, getCheckbox(task))
row.setOnClickFillInIntent(
R.id.widget_complete_box,
Intent(WidgetClickActivity.COMPLETE_TASK)
.putExtra(WidgetClickActivity.EXTRA_TASK, task))
} else {
row.setViewPadding(R.id.widget_complete_box, hPad, 0, 0, 0)
row.setInt(R.id.widget_complete_box, "setBackgroundResource", 0)
}
row.setViewPadding(R.id.top_padding, 0, vPad, 0, 0)
row.setViewPadding(R.id.bottom_padding, 0, vPad, 0, 0)
if (!showDividers) {
row.setViewVisibility(R.id.divider, View.GONE)
}
row.removeAllViews(R.id.chips)
if (showSubtasks && taskContainer.hasChildren()) {
val chip = chipProvider.getSubtaskChip(taskContainer)
row.addView(R.id.chips, chip)
row.setOnClickFillInIntent(
R.id.chip,
Intent(WidgetClickActivity.TOGGLE_SUBTASKS)
.putExtra(WidgetClickActivity.EXTRA_TASK, task)
.putExtra(WidgetClickActivity.EXTRA_COLLAPSED, !taskContainer.isCollapsed)
)
}
if (taskContainer.isHidden && showStartDates) {
val sortByDate = groupMode == SortHelper.SORT_START && !disableGroups
chipProvider
.getStartDateChip(taskContainer, showFullDate, sortByDate)
?.let { row.addView(R.id.chips, it) }
}
if (taskContainer.hasLocation() && showPlaces) {
chipProvider
.getPlaceChip(filter, taskContainer)
?.let { row.addView(R.id.chips, it) }
}
if (!taskContainer.hasParent() && showLists) {
chipProvider
.getListChip(filter, taskContainer)
?.let { row.addView(R.id.chips, it) }
}
if (showTags && taskContainer.tagsString?.isNotBlank() == true) {
chipProvider
.getTagChips(filter, taskContainer)
.forEach { row.addView(R.id.chips, it) }
}
val startPad = taskContainer.indent * indentPadding
row.setViewPadding(R.id.widget_row, startPad, 0, 0, 0)
return row
} catch (e: Exception) {
Timber.e(e)
}
return null
}
private fun getTask(position: Int): TaskContainer? = tasks.getItem(position)
private suspend fun getQuery(filter: Filter?): List<String> {
val queries = getQuery(widgetPreferences, filter!!)
val last = queries.size - 1
queries[last] =
subtasksHelper.applySubtasksToWidgetFilter(filter, widgetPreferences, queries[last])
return queries
}
private fun formatDueDate(row: RemoteViews, task: TaskContainer) {
val dueDateRes = if (endDueDate) R.id.widget_due_end else R.id.widget_due_bottom
row.setViewVisibility(if (endDueDate) R.id.widget_due_bottom else R.id.widget_due_end, View.GONE)
val hasDueDate = task.hasDueDate()
val endPad = if (hasDueDate && endDueDate) 0 else hPad
row.setViewPadding(R.id.widget_text, 0, 0, endPad, 0)
if (hasDueDate) {
if (endDueDate) {
row.setViewPadding(R.id.widget_due_end, hPad, vPad, hPad, vPad)
}
row.setViewVisibility(dueDateRes, View.VISIBLE)
val text = if (
groupMode == SortHelper.SORT_DUE &&
(task.sortGroup ?: 0L) >= now().startOfDay() &&
!disableGroups
) {
task.takeIf { it.hasDueTime() }?.let {
DateUtilities.getTimeString(context, DateTimeUtils.newDateTime(task.dueDate))
}
} else {
DateUtilities.getRelativeDateTime(
context, task.dueDate, locale, FormatStyle.MEDIUM, showFullDate, false)
}
row.setTextViewText(dueDateRes, text)
row.setTextColor(
dueDateRes,
if (task.isOverdue) context.getColor(R.color.overdue) else textColorSecondary)
row.setFloat(dueDateRes, "setTextSize", dueDateTextSize)
if (handleDueDateClick) {
row.setOnClickFillInIntent(
dueDateRes,
Intent(WidgetClickActivity.RESCHEDULE_TASK)
.putExtra(WidgetClickActivity.EXTRA_TASK, task.task))
} else {
row.setInt(dueDateRes, "setBackgroundResource", 0)
}
} else {
row.setViewVisibility(dueDateRes, View.GONE)
}
}
private suspend fun updateSettings() {
vPad = widgetPreferences.widgetSpacing
hPad = context.resources.getDimension(R.dimen.widget_padding).toInt()
handleDueDateClick = widgetPreferences.rescheduleOnDueDateClick()
showFullTaskTitle = widgetPreferences.showFullTaskTitle()
showDescription = widgetPreferences.showDescription()
showFullDescription = widgetPreferences.showFullDescription()
chipProvider.isDark = isDark
textColorPrimary = context.getColor(if (isDark) R.color.white_87 else R.color.black_87)
textColorSecondary = context.getColor(if (isDark) R.color.white_60 else R.color.black_60)
val dueDatePosition = widgetPreferences.dueDatePosition
showDueDates = dueDatePosition != 2
endDueDate = dueDatePosition != 1
showCheckboxes = widgetPreferences.showCheckboxes()
textSize = widgetPreferences.fontSize.toFloat()
dueDateTextSize = max(10f, textSize - 2)
filter = defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId)
showDividers = widgetPreferences.showDividers()
disableGroups = filter?.let {
!it.supportsSorting()
|| (it.supportsManualSort() && widgetPreferences.isManualSort)
|| (it is AstridOrderingFilter && widgetPreferences.isAstridSort)
} == true
showPlaces = widgetPreferences.showPlaces()
showSubtasks = widgetPreferences.showSubtasks()
showStartDates = widgetPreferences.showStartDates()
showLists = widgetPreferences.showLists()
showTags = widgetPreferences.showTags()
showFullDate = widgetPreferences.alwaysDisplayFullDate
widgetPreferences.groupMode.takeIf { it != groupMode }
?.let {
if (groupMode != SortHelper.GROUP_NONE) {
widgetPreferences.collapsed = mutableSetOf(HEADER_COMPLETED)
}
groupMode = it
}
subtaskMode = widgetPreferences.subtaskMode
collapsed = widgetPreferences.collapsed
compact = widgetPreferences.compact
}
init {
val metrics = context.resources.displayMetrics
indentPadding = (20 * metrics.density).toInt()
}
}

@ -1,74 +0,0 @@
package org.tasks.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.RemoteViewsService;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import org.tasks.LocalBroadcastManager;
import org.tasks.data.TaskDao;
import org.tasks.markdown.MarkdownProvider;
import org.tasks.preferences.DefaultFilterProvider;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.HeaderFormatter;
import org.tasks.themes.ColorProvider;
import org.tasks.ui.CheckBoxProvider;
import java.util.Locale;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class ScrollableWidgetUpdateService extends RemoteViewsService {
@Inject TaskDao taskDao;
@Inject Preferences preferences;
@Inject SubtasksHelper subtasksHelper;
@Inject DefaultFilterProvider defaultFilterProvider;
@Inject Locale locale;
@Inject ChipProvider chipProvider;
@Inject LocalBroadcastManager localBroadcastManager;
@Inject MarkdownProvider markdownProvider;
@Inject HeaderFormatter headerFormatter;
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
stopSelf();
}
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
if (intent == null) {
return null;
}
Bundle extras = intent.getExtras();
if (extras == null) {
return null;
}
int widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
Context context = getApplicationContext();
return new ScrollableViewsFactory(
subtasksHelper,
preferences,
context,
widgetId,
taskDao,
defaultFilterProvider,
new CheckBoxProvider(context, new ColorProvider(context, preferences)),
locale,
chipProvider,
localBroadcastManager,
markdownProvider.markdown(false),
headerFormatter
);
}
}

@ -10,7 +10,6 @@ import android.os.Bundle
import android.view.View
import android.widget.RemoteViews
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import com.todoroo.andlib.utility.AndroidUtilities.atLeastS
import com.todoroo.astrid.api.Filter
import dagger.hilt.android.AndroidEntryPoint
@ -18,7 +17,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.runBlocking
import org.tasks.R
import org.tasks.dialogs.FilterPicker
import org.tasks.extensions.Context.isNightMode
import org.tasks.extensions.setBackgroundColor
import org.tasks.extensions.setColorFilter
import org.tasks.extensions.setRipple
import org.tasks.intents.TaskIntents
import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences
@ -33,10 +34,13 @@ class TasksWidget : AppWidgetProvider() {
@Inject @ApplicationContext lateinit var context: Context
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
for (id in appWidgetIds) {
appWidgetIds.forEach { appWidgetId ->
try {
val options = appWidgetManager.getAppWidgetOptions(id)
appWidgetManager.updateAppWidget(id, createScrollableWidget(context, id, options))
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
appWidgetManager.updateAppWidget(
appWidgetId,
createWidget(context, appWidgetId, options)
)
} catch (e: Exception) {
Timber.e(e)
}
@ -47,97 +51,123 @@ class TasksWidget : AppWidgetProvider() {
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
newOptions: Bundle?
newOptions: Bundle
) {
newOptions?.let {
appWidgetManager
.updateAppWidget(appWidgetId, createScrollableWidget(context, appWidgetId, it))
}
appWidgetManager.updateAppWidget(
appWidgetId,
createWidget(context, appWidgetId, newOptions)
)
}
private fun createScrollableWidget(context: Context, id: Int, options: Bundle): RemoteViews {
private fun createWidget(context: Context, id: Int, options: Bundle): RemoteViews {
val widgetPreferences = WidgetPreferences(context, preferences, id)
widgetPreferences.compact =
options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) < COMPACT_MAX
val filterId = widgetPreferences.filterId
val color = ThemeColor(context, widgetPreferences.color)
val remoteViews = RemoteViews(context.packageName, R.layout.scrollable_widget)
if (widgetPreferences.showHeader()) {
remoteViews.setViewVisibility(R.id.widget_header, View.VISIBLE)
remoteViews.setViewVisibility(
R.id.widget_change_list,
if (widgetPreferences.showMenu()) View.VISIBLE else View.GONE
val settings = widgetPreferences.getWidgetHeaderSettings()
widgetPreferences.setCompact(
options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) < COMPACT_MAX
)
val filter = runBlocking {
defaultFilterProvider.getFilterFromPreference(widgetPreferences.filterId)
}
return RemoteViews(context.packageName, R.layout.scrollable_widget).apply {
if (settings.showHeader) {
setViewVisibility(R.id.widget_header, View.VISIBLE)
setupHeader(settings, filter, id)
} else {
setViewVisibility(R.id.widget_header, View.GONE)
}
val bgColor = getBackgroundColor(widgetPreferences.themeIndex)
setBackgroundColor(
viewId = R.id.list_view,
color = bgColor,
opacity = widgetPreferences.rowOpacity,
)
remoteViews.setViewVisibility(
R.id.widget_reconfigure,
if (widgetPreferences.showSettings()) View.VISIBLE else View.GONE
setBackgroundColor(
viewId = R.id.empty_view,
color = bgColor,
opacity = widgetPreferences.footerOpacity,
)
remoteViews.removeAllViews(R.id.title_container)
remoteViews.addView(
R.id.title_container,
RemoteViews(context.packageName, widgetPreferences.headerLayout)
setOnClickPendingIntent(R.id.empty_view, getOpenListIntent(context, filter, id))
val cacheBuster = Uri.parse("tasks://widget/" + System.currentTimeMillis())
setRemoteAdapter(
R.id.list_view,
Intent(context, TasksWidgetAdapter::class.java)
.putExtra(TasksWidgetAdapter.EXTRA_FILTER, filter)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
.setData(cacheBuster)
)
val widgetPadding = context.resources.getDimension(R.dimen.widget_padding).toInt()
val widgetTitlePadding = if (widgetPreferences.showMenu()) 0 else widgetPadding
val vPad = widgetPreferences.headerSpacing
remoteViews.setViewPadding(R.id.widget_title, widgetTitlePadding, 0, 0, 0)
remoteViews.setInt(R.id.widget_title, "setTextColor", color.colorOnPrimary)
buttons.forEach {
remoteViews.setInt(it, "setColorFilter", color.colorOnPrimary)
remoteViews.setViewPadding(it, widgetPadding, vPad, widgetPadding, vPad)
}
} else {
remoteViews.setViewVisibility(R.id.widget_header, View.GONE)
setPendingIntentTemplate(R.id.list_view, getPendingIntentTemplate(context))
}
remoteViews.setInt(
R.id.widget_header,
"setBackgroundColor",
ColorUtils.setAlphaComponent(color.primaryColor, widgetPreferences.headerOpacity))
val bgColor = getBackgroundColor(widgetPreferences.themeIndex)
remoteViews.setInt(
R.id.list_view,
"setBackgroundColor",
ColorUtils.setAlphaComponent(bgColor, widgetPreferences.rowOpacity))
remoteViews.setInt(
R.id.empty_view,
"setBackgroundColor",
ColorUtils.setAlphaComponent(bgColor, widgetPreferences.footerOpacity))
val filter = runBlocking { defaultFilterProvider.getFilterFromPreference(filterId) }
remoteViews.setTextViewText(R.id.widget_title, if (widgetPreferences.showTitle()) {
filter.title
} else {
null
})
val cacheBuster = Uri.parse("tasks://widget/" + System.currentTimeMillis())
remoteViews.setRemoteAdapter(
R.id.list_view,
Intent(context, ScrollableWidgetUpdateService::class.java)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
.setData(cacheBuster))
setRipple(
remoteViews, color, R.id.widget_button, R.id.widget_change_list, R.id.widget_reconfigure)
remoteViews.setOnClickPendingIntent(R.id.widget_title, getOpenListIntent(context, filter, id))
remoteViews.setViewVisibility(
R.id.widget_button,
if (filter.isWritable) View.VISIBLE else View.GONE
}
private fun RemoteViews.setupHeader(
widgetPreferences: WidgetPreferences.WidgetHeaderSettings,
filter: Filter,
id: Int,
) {
val color = ThemeColor(context, widgetPreferences.color)
setBackgroundColor(
viewId = R.id.widget_header,
color = color.primaryColor,
opacity = widgetPreferences.headerOpacity,
)
val hPad = context.resources.getDimension(R.dimen.widget_padding).toInt()
val vPad = widgetPreferences.headerSpacing
setupButton(
viewId = R.id.widget_change_list,
enabled = widgetPreferences.showMenu,
color = color,
vPad = vPad,
hPad = hPad,
onClick = getChooseListIntent(context, filter, id),
)
setupButton(
viewId = R.id.widget_reconfigure,
enabled = widgetPreferences.showSettings,
color = color,
vPad = vPad,
hPad = hPad,
onClick = getWidgetConfigIntent(context, id),
)
setupButton(
viewId = R.id.widget_button,
enabled = filter.isWritable,
color = color,
vPad = vPad,
hPad = hPad,
onClick = getNewTaskIntent(context, filter, id),
)
addView(
R.id.title_container,
RemoteViews(context.packageName, widgetPreferences.headerLayout)
)
setViewPadding(
R.id.widget_title,
if (widgetPreferences.showMenu) 0 else hPad, 0, 0, 0
)
setTextColor(R.id.widget_title, color.colorOnPrimary)
setOnClickPendingIntent(R.id.widget_title, getOpenListIntent(context, filter, id))
setTextViewText(
R.id.widget_title,
if (widgetPreferences.showTitle) filter.title else null
)
remoteViews.setOnClickPendingIntent(R.id.widget_button, getNewTaskIntent(context, filter, id))
remoteViews.setOnClickPendingIntent(R.id.widget_change_list, getChooseListIntent(context, filter, id))
remoteViews.setOnClickPendingIntent(
R.id.widget_reconfigure, getWidgetConfigIntent(context, id))
if (widgetPreferences.openOnFooterClick()) {
remoteViews.setOnClickPendingIntent(R.id.empty_view, getOpenListIntent(context, filter, id))
} else {
remoteViews.setOnClickPendingIntent(R.id.empty_view, null)
}
remoteViews.setPendingIntentTemplate(R.id.list_view, getPendingIntentTemplate(context))
return remoteViews
}
private fun setRipple(rv: RemoteViews, color: ThemeColor, vararg views: Int) {
val drawableRes = if (color.isDark) R.drawable.widget_ripple_circle_light else R.drawable.widget_ripple_circle_dark
for (view in views) {
rv.setInt(view, "setBackgroundResource", drawableRes)
private fun RemoteViews.setupButton(
viewId: Int,
enabled: Boolean,
color: ThemeColor,
vPad: Int,
hPad: Int,
onClick: PendingIntent,
) {
if (enabled) {
setViewVisibility(viewId, View.VISIBLE)
setColorFilter(viewId, color.colorOnPrimary)
setViewPadding(viewId, hPad, vPad, hPad, vPad)
setRipple(viewId, color.isDark)
setOnClickPendingIntent(viewId, onClick)
} else {
setViewVisibility(viewId, View.GONE)
}
}
@ -146,7 +176,7 @@ class TasksWidget : AppWidgetProvider() {
val background: Int = when (themeIndex) {
1 -> android.R.color.black
2 -> R.color.md_background_dark
3 -> if (context.isNightMode) R.color.md_background_dark else android.R.color.white
3 -> R.color.widget_background_follow_system
else -> android.R.color.white
}
return context.getColor(background)
@ -187,7 +217,7 @@ class TasksWidget : AppWidgetProvider() {
private fun getWidgetConfigIntent(context: Context, widgetId: Int): PendingIntent {
val intent = Intent(context, WidgetConfigActivity::class.java)
intent.flags = flags
intent.flags = FLAGS
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
intent.action = "widget_settings"
return PendingIntent.getActivity(
@ -200,7 +230,7 @@ class TasksWidget : AppWidgetProvider() {
private fun getChooseListIntent(context: Context, filter: Filter, widgetId: Int): PendingIntent {
val intent = Intent(context, WidgetFilterSelectionActivity::class.java)
intent.flags = flags
intent.flags = FLAGS
intent.putExtra(FilterPicker.EXTRA_FILTER, filter)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
intent.action = "choose_list"
@ -214,9 +244,6 @@ class TasksWidget : AppWidgetProvider() {
companion object {
private const val COMPACT_MAX = 275
private const val flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
private val buttons = intArrayOf(
R.id.widget_change_list, R.id.widget_button, R.id.widget_reconfigure
)
private const val FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
}

@ -0,0 +1,47 @@
package org.tasks.widget
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.widget.RemoteViewsService
import androidx.core.content.IntentCompat
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.subtasks.SubtasksHelper
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.data.TaskDao
import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.Preferences
import org.tasks.tasklist.HeaderFormatter
import java.util.Locale
import javax.inject.Inject
@AndroidEntryPoint
class TasksWidgetAdapter : RemoteViewsService() {
@Inject lateinit var taskDao: TaskDao
@Inject lateinit var preferences: Preferences
@Inject lateinit var subtasksHelper: SubtasksHelper
@Inject lateinit var locale: Locale
@Inject lateinit var chipProvider: WidgetChipProvider
@Inject lateinit var markdownProvider: MarkdownProvider
@Inject lateinit var headerFormatter: HeaderFormatter
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory? {
val widgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return null
val filter = IntentCompat.getParcelableExtra(intent, EXTRA_FILTER, Filter::class.java) ?: return null
return TasksWidgetViewFactory(
subtasksHelper,
preferences,
filter,
applicationContext,
widgetId,
taskDao,
locale,
chipProvider,
markdownProvider.markdown(false),
headerFormatter,
)
}
companion object {
const val EXTRA_FILTER = "extra_filter"
}
}

@ -0,0 +1,321 @@
package org.tasks.widget
import android.content.Context
import android.content.Intent
import android.view.View
import android.widget.RemoteViews
import android.widget.RemoteViewsService.RemoteViewsFactory
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.SortHelper
import com.todoroo.astrid.subtasks.SubtasksHelper
import kotlinx.coroutines.runBlocking
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.data.TaskContainer
import org.tasks.data.TaskDao
import org.tasks.data.TaskListQuery.getQuery
import org.tasks.date.DateTimeUtils
import org.tasks.extensions.Context.isNightMode
import org.tasks.extensions.setBackgroundResource
import org.tasks.extensions.setColorFilter
import org.tasks.extensions.setMaxLines
import org.tasks.extensions.setTextSize
import org.tasks.extensions.strikethrough
import org.tasks.markdown.Markdown
import org.tasks.preferences.Preferences
import org.tasks.tasklist.HeaderFormatter
import org.tasks.tasklist.SectionedDataSource
import org.tasks.themes.ColorProvider.Companion.priorityColor
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.ui.CheckBoxProvider.Companion.getCheckboxRes
import timber.log.Timber
import java.time.format.FormatStyle
import java.util.Locale
import kotlin.math.max
internal class TasksWidgetViewFactory(
private val subtasksHelper: SubtasksHelper,
preferences: Preferences,
private val filter: Filter,
private val context: Context,
private val widgetId: Int,
private val taskDao: TaskDao,
private val locale: Locale,
private val chipProvider: WidgetChipProvider,
private val markdown: Markdown,
private val headerFormatter: HeaderFormatter,
) : RemoteViewsFactory {
private val indentPadding = (20 * context.resources.displayMetrics.density).toInt()
private val widgetPreferences = WidgetPreferences(context, preferences, widgetId)
private val settings = widgetPreferences.getWidgetListSettings()
private val hPad = context.resources.getDimension(R.dimen.widget_padding).toInt()
private val disableGroups = !filter.supportsSorting()
|| (filter.supportsManualSort() && widgetPreferences.isManualSort)
|| (filter is AstridOrderingFilter && widgetPreferences.isAstridSort)
private var tasks = SectionedDataSource()
private val isDark = when (widgetPreferences.themeIndex) {
0 -> false
3 -> context.isNightMode
else -> true
}
private val onSurface = context.getColor(if (isDark) R.color.white_87 else R.color.black_87)
private val onSurfaceVariant = context.getColor(if (isDark) R.color.white_60 else R.color.black_60)
init {
chipProvider.isDark = isDark
}
override fun onCreate() {}
override fun onDataSetChanged() {
runBlocking {
val collapsed = widgetPreferences.collapsed
tasks = SectionedDataSource(
taskDao.fetchTasks { getQuery(filter) },
disableGroups,
settings.groupMode,
widgetPreferences.subtaskMode,
collapsed,
widgetPreferences.completedTasksAtBottom,
)
collapsed.toMutableSet().let {
if (it.retainAll(tasks.getSectionValues().toSet())) {
widgetPreferences.collapsed = it
}
}
}
}
override fun onDestroy() {}
override fun getCount() = tasks.size
override fun getViewAt(position: Int): RemoteViews? =
if (tasks.isHeader(position)) buildHeader(position) else buildUpdate(position)
override fun getLoadingView(): RemoteViews = newRemoteView()
override fun getViewTypeCount(): Int = 2
override fun getItemId(position: Int) = getTask(position)?.id ?: 0
override fun hasStableIds(): Boolean = true
private fun newRemoteView() = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_row)
private fun buildHeader(position: Int): RemoteViews {
val section = tasks.getSection(position)
val sortGroup = section.value
val header: String? = if (filter.supportsSorting()) {
headerFormatter.headerStringBlocking(
value = section.value,
groupMode = settings.groupMode,
alwaysDisplayFullDate = settings.showFullDate,
style = FormatStyle.MEDIUM,
compact = settings.compact,
)
} else {
null
}
return RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_header).apply {
setTextViewText(R.id.header, header)
setImageViewResource(
R.id.arrow, if (section.collapsed) {
R.drawable.ic_keyboard_arrow_down_black_18dp
} else {
R.drawable.ic_keyboard_arrow_up_black_18dp
}
)
setTextColor(
R.id.header,
section.headerColor(
context,
settings.groupMode,
if (isDark) R.color.white_60 else R.color.black_60
)
)
if (!settings.showDividers) {
setViewVisibility(R.id.divider, View.GONE)
}
setOnClickFillInIntent(
R.id.row,
Intent(WidgetClickActivity.TOGGLE_GROUP)
.putExtra(WidgetClickActivity.EXTRA_WIDGET, widgetId)
.putExtra(WidgetClickActivity.EXTRA_GROUP, sortGroup)
.putExtra(WidgetClickActivity.EXTRA_COLLAPSED, !section.collapsed)
)
}
}
private fun buildUpdate(position: Int): RemoteViews? {
return try {
val taskContainer = getTask(position) ?: return null
val task = taskContainer.task
val textColorTitle = when {
task.isHidden -> onSurfaceVariant
task.isCompleted -> onSurfaceVariant
!settings.showDueDates && task.isOverdue -> context.getColor(R.color.overdue)
else -> onSurface
}
newRemoteView().apply {
strikethrough(R.id.widget_text, task.isCompleted)
setTextSize(R.id.widget_text, settings.textSize)
if (settings.showDueDates) {
formatDueDate(this, taskContainer)
} else {
setViewVisibility(R.id.widget_due_bottom, View.GONE)
setViewVisibility(R.id.widget_due_end, View.GONE)
}
if (settings.showFullTaskTitle) {
setMaxLines(R.id.widget_text, Int.MAX_VALUE)
}
setTextViewText(
R.id.widget_text,
markdown.toMarkdown(task.title)
)
setTextColor(R.id.widget_text, textColorTitle)
if (settings.showDescription && task.hasNotes()) {
setTextSize(R.id.widget_description, settings.textSize)
setTextColor(R.id.widget_description, onSurfaceVariant)
setTextViewText(
R.id.widget_description,
markdown.toMarkdown(task.notes)
)
setViewVisibility(R.id.widget_description, View.VISIBLE)
if (settings.showFullDescription) {
setMaxLines(R.id.widget_description, Int.MAX_VALUE)
}
} else {
setViewVisibility(R.id.widget_description, View.GONE)
}
setOnClickFillInIntent(
R.id.widget_row,
Intent(WidgetClickActivity.EDIT_TASK)
.putExtra(WidgetClickActivity.EXTRA_FILTER, filter)
.putExtra(WidgetClickActivity.EXTRA_TASK, task)
)
if (settings.showCheckboxes) {
setViewPadding(
R.id.widget_complete_box,
hPad,
settings.vPad,
hPad,
settings.vPad
)
setImageViewResource(R.id.widget_complete_box, task.getCheckboxRes())
setColorFilter(R.id.widget_complete_box, priorityColor(task.priority))
setOnClickFillInIntent(
R.id.widget_complete_box,
Intent(WidgetClickActivity.COMPLETE_TASK)
.putExtra(WidgetClickActivity.EXTRA_TASK, task)
)
} else {
setViewPadding(R.id.widget_complete_box, hPad, 0, 0, 0)
setBackgroundResource(R.id.widget_complete_box, 0)
}
setViewPadding(R.id.top_padding, 0, settings.vPad, 0, 0)
setViewPadding(R.id.bottom_padding, 0, settings.vPad, 0, 0)
if (!settings.showDividers) {
setViewVisibility(R.id.divider, View.GONE)
}
removeAllViews(R.id.chips)
if (settings.showSubtaskChips && taskContainer.hasChildren()) {
val chip = chipProvider.getSubtaskChip(taskContainer)
addView(R.id.chips, chip)
setOnClickFillInIntent(
R.id.chip,
Intent(WidgetClickActivity.TOGGLE_SUBTASKS)
.putExtra(WidgetClickActivity.EXTRA_TASK, task)
.putExtra(
WidgetClickActivity.EXTRA_COLLAPSED,
!taskContainer.isCollapsed
)
)
}
if (taskContainer.isHidden && settings.showStartChips) {
val sortByDate = settings.groupMode == SortHelper.SORT_START && !disableGroups
chipProvider
.getStartDateChip(taskContainer, settings.showFullDate, sortByDate)
?.let { addView(R.id.chips, it) }
}
if (taskContainer.hasLocation() && settings.showPlaceChips) {
chipProvider
.getPlaceChip(filter, taskContainer)
?.let { addView(R.id.chips, it) }
}
if (!taskContainer.hasParent() && settings.showListChips) {
chipProvider
.getListChip(filter, taskContainer)
?.let { addView(R.id.chips, it) }
}
if (settings.showTagChips && taskContainer.tagsString?.isNotBlank() == true) {
chipProvider
.getTagChips(filter, taskContainer)
.forEach { addView(R.id.chips, it) }
}
val startPad = taskContainer.indent * indentPadding
setViewPadding(R.id.widget_row, startPad, 0, 0, 0)
}
} catch (e: Exception) {
Timber.e(e)
null
}
}
private fun getTask(position: Int): TaskContainer? = tasks.getItem(position)
private suspend fun getQuery(filter: Filter): List<String> {
val queries = getQuery(widgetPreferences, filter)
val last = queries.size - 1
queries[last] =
subtasksHelper.applySubtasksToWidgetFilter(filter, widgetPreferences, queries[last])
return queries
}
private fun formatDueDate(row: RemoteViews, task: TaskContainer) = with(row) {
val dueDateRes = if (settings.endDueDate) R.id.widget_due_end else R.id.widget_due_bottom
setViewVisibility(
if (settings.endDueDate) R.id.widget_due_bottom else R.id.widget_due_end,
View.GONE
)
val hasDueDate = task.hasDueDate()
val endPad = if (hasDueDate && settings.endDueDate) 0 else hPad
setViewPadding(R.id.widget_text, 0, 0, endPad, 0)
if (hasDueDate) {
if (settings.endDueDate) {
setViewPadding(R.id.widget_due_end, hPad, settings.vPad, hPad, settings.vPad)
}
setViewVisibility(dueDateRes, View.VISIBLE)
val text = if (
settings.groupMode == SortHelper.SORT_DUE &&
(task.sortGroup ?: 0L) >= now().startOfDay() &&
!disableGroups
) {
task.takeIf { it.hasDueTime() }?.let {
DateUtilities.getTimeString(context, DateTimeUtils.newDateTime(task.dueDate))
}
} else {
DateUtilities.getRelativeDateTime(
context, task.dueDate, locale, FormatStyle.MEDIUM, settings.showFullDate, false
)
}
setTextViewText(dueDateRes, text)
setTextColor(
dueDateRes,
if (task.isOverdue) context.getColor(R.color.overdue) else onSurfaceVariant
)
setTextSize(dueDateRes, max(10f, settings.textSize - 2))
setOnClickFillInIntent(
dueDateRes,
Intent(WidgetClickActivity.RESCHEDULE_TASK)
.putExtra(WidgetClickActivity.EXTRA_TASK, task.task)
)
} else {
setViewVisibility(dueDateRes, View.GONE)
}
}
}

@ -2,6 +2,7 @@ package org.tasks.widget
import android.content.Context
import android.widget.RemoteViews
import androidx.annotation.ColorInt
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter
@ -13,6 +14,7 @@ import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.data.TaskContainer
import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.extensions.setColorFilter
import org.tasks.filters.PlaceFilter
import org.tasks.themes.CustomIcons
import org.tasks.time.DateTimeUtils.startOfDay
@ -21,7 +23,7 @@ import java.time.format.FormatStyle
import java.util.Locale
import javax.inject.Inject
class ChipProvider @Inject constructor(
class WidgetChipProvider @Inject constructor(
@ApplicationContext private val context: Context,
private val chipListCache: ChipListCache,
private val locale: Locale,
@ -29,51 +31,51 @@ class ChipProvider @Inject constructor(
var isDark = false
fun getSubtaskChip(task: TaskContainer): RemoteViews {
val chip = newChip()
chip.setTextViewText(
return newChip().apply {
setTextViewText(
R.id.chip_text,
context
.resources
.getQuantityString(R.plurals.subtask_count, task.children, task.children)
)
chip.setImageViewResource(
.resources
.getQuantityString(R.plurals.subtask_count, task.children, task.children)
)
setImageViewResource(
R.id.chip_icon,
if (task.isCollapsed) {
R.drawable.ic_keyboard_arrow_down_black_24dp
} else {
R.drawable.ic_keyboard_arrow_up_black_24dp
}
)
return chip
)
}
}
fun getStartDateChip(task: TaskContainer, showFullDate: Boolean, sortByStartDate: Boolean): RemoteViews? {
return if (task.isHidden) {
val chip = newChip()
val time = if (sortByStartDate && task.sortGroup?.startOfDay() == task.task.hideUntil.startOfDay()) {
task.task.hideUntil
.takeIf { Task.hasDueTime(it) }
?.let { DateUtilities.getTimeString(context, it.toDateTime()) }
?: return null
.takeIf { Task.hasDueTime(it) }
?.let { DateUtilities.getTimeString(context, it.toDateTime()) }
?: return null
} else {
DateUtilities.getRelativeDateTime(
context,
task.task.hideUntil,
locale,
FormatStyle.MEDIUM,
showFullDate,
false
context,
task.task.hideUntil,
locale,
FormatStyle.MEDIUM,
showFullDate,
false
)
}
chip.setTextViewText(R.id.chip_text, time)
chip.setImageViewResource(R.id.chip_icon, R.drawable.ic_pending_actions_24px)
chip
newChip().apply {
setTextViewText(R.id.chip_text, time)
setImageViewResource(R.id.chip_icon, R.drawable.ic_pending_actions_24px)
}
} else {
null
}
}
fun getListChip(filter: Filter?, task: TaskContainer): RemoteViews? {
fun getListChip(filter: Filter, task: TaskContainer): RemoteViews? {
return task.caldav
?.takeIf { filter !is CaldavFilter && filter !is GtasksFilter }
?.let { chipListCache.getCaldavList(it) }
@ -85,14 +87,14 @@ class ChipProvider @Inject constructor(
}
}
fun getPlaceChip(filter: Filter?, task: TaskContainer): RemoteViews? {
fun getPlaceChip(filter: Filter, task: TaskContainer): RemoteViews? {
task.location
?.takeIf { filter !is PlaceFilter || it.place != filter.place}
?.let { return newChip(PlaceFilter(it.place), R.drawable.ic_outline_place_24px) }
return null
}
fun getTagChips(filter: Filter?, task: TaskContainer): List<RemoteViews> {
fun getTagChips(filter: Filter, task: TaskContainer): List<RemoteViews> {
val tags = task.tagsString?.split(",")?.toHashSet() ?: return emptyList()
if (filter is TagFilter) {
tags.remove(filter.uuid)
@ -100,30 +102,29 @@ class ChipProvider @Inject constructor(
return tags
.mapNotNull(chipListCache::getTag)
.sortedBy(TagFilter::title)
.mapNotNull { newChip(it, R.drawable.ic_outline_label_24px) }
.map { newChip(it, R.drawable.ic_outline_label_24px) }
}
private fun newChip(filter: Filter?, defaultIcon: Int): RemoteViews? {
if (filter == null) {
return null
}
val chip = newChip()
chip.setTextViewText(R.id.chip_text, filter.title)
val icon = filter.icon
private fun newChip(filter: Filter, defaultIcon: Int) =
newChip(filter.tint).apply {
setTextViewText(R.id.chip_text, filter.title)
val icon = filter.icon
.takeIf { it >= 0 }
?.let { CustomIcons.getIconResId(it) }
?: defaultIcon
chip.setImageViewResource(R.id.chip_icon, icon)
if (filter.tint != 0) {
chip.setInt(R.id.chip_background, "setColorFilter", filter.tint)
chip.setTextColor(R.id.chip_text, filter.tint)
chip.setInt(R.id.chip_icon, "setColorFilter", filter.tint)
setImageViewResource(R.id.chip_icon, icon)
}
return chip
}
private fun newChip() = RemoteViews(
BuildConfig.APPLICATION_ID,
if (isDark) R.layout.widget_chip_dark else R.layout.widget_chip_light
)
private fun newChip(@ColorInt color: Int = 0) = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_chip).apply {
val tint = if (color == 0) {
context.getColor(
if (isDark) R.color.icon_tint_dark_alpha else R.color.icon_tint_light_alpha
)
} else {
color
}
setColorFilter(R.id.chip_icon, tint)
setColorFilter(R.id.chip_background, tint)
setTextColor(R.id.chip_text, tint)
}
}

@ -67,7 +67,7 @@ class WidgetClickActivity : AppCompatActivity(), OnDismissHandler {
preferences,
intent.getIntExtra(EXTRA_WIDGET, -1)
)
val collapsed = widgetPreferences.collapsed
val collapsed = widgetPreferences.collapsed.toMutableSet()
val group = intent.getLongExtra(EXTRA_GROUP, -1)
if (intent.getBooleanExtra(EXTRA_COLLAPSED, false)) {
collapsed.add(group)

@ -6,7 +6,7 @@ import android.os.Bundle
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.preferences.BasePreferences
import org.tasks.preferences.fragments.ScrollableWidget.Companion.newScrollableWidget
import org.tasks.preferences.fragments.WidgetSettings.Companion.newWidgetSettings
@AndroidEntryPoint
class WidgetConfigActivity : BasePreferences() {
@ -32,5 +32,5 @@ class WidgetConfigActivity : BasePreferences() {
override fun getRootTitle() = R.string.widget_settings
override fun getRootPreference() = newScrollableWidget(appWidgetId)
override fun getRootPreference() = newWidgetSettings(appWidgetId)
}

@ -8,6 +8,7 @@ import com.todoroo.astrid.service.Upgrader.Companion.getLegacyColor
import org.tasks.R
import org.tasks.preferences.Preferences
import org.tasks.preferences.QueryPreferences
import org.tasks.tasklist.SectionedDataSource.Companion.HEADER_COMPLETED
import timber.log.Timber
class WidgetPreferences(
@ -15,65 +16,77 @@ class WidgetPreferences(
private val preferences: Preferences,
private val widgetId: Int
) : QueryPreferences {
fun showHeader(): Boolean {
return getBoolean(R.string.p_widget_show_header, true)
}
fun showTitle(): Boolean {
return getBoolean(R.string.p_widget_show_title, true)
}
fun showCheckboxes(): Boolean {
return getBoolean(R.string.p_widget_show_checkboxes, true)
}
fun showSettings(): Boolean {
return getBoolean(R.string.p_widget_show_settings, true)
}
fun showMenu(): Boolean {
return getBoolean(R.string.p_widget_show_menu, true)
}
fun showFullTaskTitle(): Boolean {
return getBoolean(R.string.p_widget_show_full_task_title, false)
}
fun showDescription(): Boolean {
return getBoolean(R.string.p_widget_show_description, true)
}
fun showFullDescription(): Boolean {
return getBoolean(R.string.p_widget_show_full_description, false)
}
fun showDividers(): Boolean {
return getBoolean(R.string.p_widget_show_dividers, true)
}
fun showSubtasks(): Boolean {
return getBoolean(R.string.p_widget_show_subtasks, true)
}
fun showStartDates(): Boolean {
return getBoolean(R.string.p_widget_show_start_dates, true)
}
fun showPlaces(): Boolean {
return getBoolean(R.string.p_widget_show_places, true)
}
fun showLists(): Boolean {
return getBoolean(R.string.p_widget_show_lists, true)
}
fun showTags(): Boolean {
return getBoolean(R.string.p_widget_show_tags, true)
data class WidgetHeaderSettings(
val showHeader: Boolean,
val showTitle: Boolean,
val showSettings: Boolean,
val showMenu: Boolean,
val color: Int,
val headerOpacity: Int,
val headerSpacing: Int,
val headerLayout: Int,
)
data class WidgetRowSettings(
val showFullTaskTitle: Boolean,
val showCheckboxes: Boolean,
val showDescription: Boolean,
val showFullDescription: Boolean,
val showDividers: Boolean,
val showSubtaskChips: Boolean,
val showStartChips: Boolean,
val showPlaceChips: Boolean,
val showListChips: Boolean,
val showTagChips: Boolean,
val vPad: Int,
val textSize: Float,
val showFullDate: Boolean,
val compact: Boolean,
val groupMode: Int,
val dueDatePosition: Int,
) {
val showDueDates get() = dueDatePosition != 2
val endDueDate get() = dueDatePosition != 1
}
val dueDatePosition: Int
get() = getIntegerFromString(R.string.p_widget_due_date_position)
var collapsed: MutableSet<Long>
fun getWidgetHeaderSettings() = WidgetHeaderSettings(
showHeader = getBoolean(R.string.p_widget_show_header, true),
showTitle = getBoolean(R.string.p_widget_show_title, true),
showSettings = getBoolean(R.string.p_widget_show_settings, true),
showMenu = getBoolean(R.string.p_widget_show_menu, true),
color = color,
headerOpacity = getAlphaValue(R.string.p_widget_header_opacity),
headerSpacing = getSpacing(R.string.p_widget_header_spacing),
headerLayout = when (getIntegerFromString(R.string.p_widget_header_spacing)) {
1 -> R.layout.widget_title_compact
2 -> R.layout.widget_title_none
else -> R.layout.widget_title_default
},
)
fun getWidgetListSettings() = WidgetRowSettings(
showFullTaskTitle = getBoolean(R.string.p_widget_show_full_task_title, false),
showCheckboxes = getBoolean(R.string.p_widget_show_checkboxes, true),
showDescription = getBoolean(R.string.p_widget_show_description, true),
showFullDescription = getBoolean(R.string.p_widget_show_full_description, false),
showDividers = getBoolean(R.string.p_widget_show_dividers, true),
showSubtaskChips = getBoolean(R.string.p_widget_show_subtasks, true),
showStartChips = getBoolean(R.string.p_widget_show_start_dates, true),
showPlaceChips = getBoolean(R.string.p_widget_show_places, true),
showListChips = getBoolean(R.string.p_widget_show_lists, true),
showTagChips = getBoolean(R.string.p_widget_show_tags, true),
vPad = getSpacing(R.string.p_widget_spacing),
textSize = getInt(R.string.p_widget_font_size, 16).toFloat(),
showFullDate = preferences.alwaysDisplayFullDate,
compact = getBoolean(R.string.p_widget_compact, false),
groupMode = groupMode,
dueDatePosition = dueDatePosition,
)
val dueDatePosition: Int get() = getIntegerFromString(R.string.p_widget_due_date_position)
var collapsed: Set<Long>
get() {
val value = getString(R.string.p_widget_collapsed)
val collapsed = HashSet<Long>()
@ -91,17 +104,6 @@ class WidgetPreferences(
set(collapsed) {
setString(R.string.p_widget_collapsed, Joiner.on(",").join(collapsed))
}
val widgetSpacing: Int
get() = getSpacing(R.string.p_widget_spacing)
val headerSpacing: Int
get() = getSpacing(R.string.p_widget_header_spacing)
val headerLayout: Int
get() = when (getIntegerFromString(R.string.p_widget_header_spacing)) {
1 -> R.layout.widget_title_compact
2 -> R.layout.widget_title_none
else -> R.layout.widget_title_default
}
private fun getSpacing(pref: Int): Int {
val spacing = getIntegerFromString(pref)
if (spacing == 2) {
@ -111,13 +113,11 @@ class WidgetPreferences(
return context.resources.getDimension(dimen).toInt()
}
val fontSize: Int
get() = getInt(R.string.p_widget_font_size, 16)
val filterId: String?
get() = getString(R.string.p_widget_filter)
val themeIndex: Int
get() = getInt(R.string.p_widget_theme, 3)
var color: Int
val color: Int
get() {
var color = getInt(R.string.p_widget_color_v2, 0)
if (color != 0) {
@ -128,24 +128,14 @@ class WidgetPreferences(
setInt(R.string.p_widget_color_v2, color)
return color
}
set(color) {
setInt(R.string.p_widget_color_v2, color)
}
val headerOpacity: Int
get() = getAlphaValue(R.string.p_widget_header_opacity)
fun setColor(color: Int) {
setInt(R.string.p_widget_color_v2, color)
}
val footerOpacity: Int
get() = getAlphaValue(R.string.p_widget_footer_opacity)
val rowOpacity: Int
get() = getAlphaValue(R.string.p_widget_opacity)
fun openOnFooterClick(): Boolean {
return getIntegerFromString(R.string.p_widget_footer_click) == 1
}
fun rescheduleOnDueDateClick(): Boolean {
return getIntegerFromString(R.string.p_widget_due_date_click) == 0
}
private fun getAlphaValue(resId: Int): Int {
return (getInt(resId, 100) / 100.0 * 255.0).toInt()
}
@ -159,15 +149,13 @@ class WidgetPreferences(
}
fun setFilter(filterPreferenceValue: String?) {
collapsed = HashSet()
collapsed = setOf(HEADER_COMPLETED)
preferences.setString(getKey(R.string.p_widget_filter), filterPreferenceValue)
}
var compact: Boolean
get() = getBoolean(R.string.p_widget_compact, false)
set(value) {
setBoolean(R.string.p_widget_compact, value)
}
fun setCompact(compact: Boolean) {
setBoolean(R.string.p_widget_compact, compact)
}
private fun getInt(resId: Int, defValue: Int): Int {
return preferences.getInt(getKey(resId), defValue)
@ -246,11 +234,8 @@ class WidgetPreferences(
get() = getBoolean(R.string.p_widget_show_hidden, true)
override val showCompleted: Boolean
get() = getBoolean(R.string.p_widget_show_completed, false)
override var alwaysDisplayFullDate: Boolean
override val alwaysDisplayFullDate: Boolean
get() = preferences.alwaysDisplayFullDate
set(noWeekday) {
preferences.alwaysDisplayFullDate = noWeekday
}
override var completedTasksAtBottom: Boolean
get() = preferences.completedTasksAtBottom
set(value) {

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="12dp" />
<stroke android:width="@dimen/chip_stroke" android:color="@color/black_60" />
<solid android:color="@android:color/transparent"/>
</shape>

@ -39,7 +39,7 @@
<ImageView
android:id="@+id/chip_background"
android:src="@drawable/widget_chip_dark_bg"
android:src="@drawable/widget_chip_bg"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignBottom="@id/chip_text"

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginEnd="2dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/chip_icon"
android:layout_width="@dimen/chip_icon_size"
android:layout_height="@dimen/chip_icon_size"
android:layout_alignTop="@id/chip_text"
android:layout_alignBottom="@id/chip_text"
android:paddingStart="5dp"
android:paddingEnd="0dp"
android:layout_gravity="center_vertical"
android:tint="@color/icon_tint_light_alpha"
tools:src="@drawable/ic_keyboard_arrow_up_black_24dp"
tools:ignore="UseAppTint" />
<TextView
android:id="@+id/chip_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_toEndOf="@id/chip_icon"
android:textSize="14sp"
android:maxLines="1"
android:ellipsize="end"
android:paddingBottom="2dp"
android:paddingStart="2dp"
android:paddingEnd="8dp"
android:textColor="@color/black_60"
tools:text="4 subtasks"/>
<ImageView
android:id="@+id/chip_background"
android:src="@drawable/widget_chip_light_bg"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignBottom="@id/chip_text"
android:layout_alignEnd="@id/chip_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:background="@color/md_background_dark">
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/half_keyline_first"
android:paddingStart="0dp"
android:paddingEnd="@dimen/keyline_first"
android:paddingBottom="@dimen/half_keyline_first"
tools:src="@drawable/ic_keyboard_arrow_down_black_18dp"
android:tint="@color/icon_tint_dark_alpha"
android:layout_alignParentEnd="true"
tools:ignore="UseAppTint" />
<TextView
android:id="@+id/header"
style="@style/TextAppearance"
android:paddingTop="@dimen/half_keyline_first"
android:paddingBottom="@dimen/half_keyline_first"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/arrow"
android:paddingStart="@dimen/keyline_first"
android:paddingEnd="0dp"
android:gravity="start|center_vertical"
android:textSize="@dimen/sku_details_row_text_size"
android:maxLines="1"
android:ellipsize="end"
tools:textColor="@color/white_60"
tools:text="Today"
app:fontFamily="sans-serif-medium" />
<ImageView
android:id="@+id/divider"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_below="@id/header"
android:scaleType="fitXY"
android:layout_height=".5dp"
android:background="@color/white_12"
tools:ignore="ContentDescription" />
</RelativeLayout>

@ -1,123 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widget_row"
android:background="@drawable/widget_ripple_dark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="@color/md_background_dark">
<ImageView
android:id="@+id/widget_complete_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:padding="@dimen/widget_padding"
android:background="@drawable/widget_ripple_circle_dark"
android:soundEffectsEnabled="false"
tools:src="@drawable/ic_outline_check_box_outline_blank_24px"
tools:tint="@color/grey_500"/>
<ImageView
android:id="@+id/top_padding"
android:layout_alignParentTop="true"
android:layout_alignStart="@id/widget_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/widget_padding"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/widget_due_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/widget_text"
android:layout_alignParentEnd="true"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:singleLine="true"
android:textAlignment="viewStart"
android:textSize="14sp"
android:background="@drawable/widget_ripple_circle_dark"
tools:text="Tomorrow"
tools:paddingEnd="@dimen/widget_padding"
tools:textColor="@color/white_60"/>
<TextView
android:id="@+id/widget_text"
android:layout_toEndOf="@id/widget_complete_box"
android:layout_toStartOf="@id/widget_due_end"
android:layout_below="@id/top_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/widget_padding"
android:paddingStart="0dp"
android:gravity="start|center_vertical"
android:maxLines="1"
android:ellipsize="end"
android:textAlignment="viewStart"
android:textSize="16sp"
tools:text="Task title"
tools:textColor="@color/white_87"/>
<TextView
android:id="@+id/widget_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/widget_text"
android:layout_toEndOf="@id/widget_complete_box"
android:paddingTop="2dp"
android:paddingStart="0dp"
android:paddingEnd="@dimen/widget_padding"
android:maxLines="2"
android:ellipsize="end"
android:textAlignment="viewStart"
android:textSize="16sp"
android:textColor="@color/white_60"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean tristique magna mauris, vel vulputate elit varius sit amet. Etiam sed nisl diam. Aenean vulputate ex nec ex condimentum commodo. Nunc faucibus augue in lacus tincidunt pretium. Donec mollis ex a ipsum semper, faucibus viverra turpis consequat. Donec id suscipit est. Duis a consectetur justo. Nunc in diam urna." />
<TextView
android:id="@+id/widget_due_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/widget_description"
android:layout_toEndOf="@id/widget_complete_box"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:singleLine="true"
android:textAlignment="viewStart"
android:textSize="14sp"
android:background="@drawable/widget_ripple_circle_dark"
tools:text="Tomorrow"
tools:textColor="@color/white_60"/>
<LinearLayout
android:id="@+id/chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/widget_due_bottom"
android:layout_toEndOf="@id/widget_complete_box"
android:paddingStart="0dp"
android:paddingEnd="@dimen/widget_padding"
android:orientation="horizontal" />
<ImageView
android:id="@+id/bottom_padding"
android:layout_below="@id/chips"
android:layout_alignStart="@id/widget_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/widget_padding"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/divider"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:scaleType="fitXY"
android:layout_height=".5dp"
android:background="@color/white_12"
tools:ignore="ContentDescription" />
</RelativeLayout>

@ -249,8 +249,6 @@
<string name="user">المستعمل</string>
<string name="copy_selected_tasks">نسخ المهام المختارة؟</string>
<string name="delete_selected_tasks">حذف المهام المختارة؟</string>
<string name="widget_do_nothing">لا تفعل شيئًا</string>
<string name="widget_on_click">عند النّقر</string>
<string name="restart_now">إعادة التشغيل الآن</string>
<string name="name_cannot_be_empty">لا يمكن أن يكون الاسم فارغًا</string>
<string name="send_anonymous_statistics">طوّر Tasks</string>
@ -400,7 +398,6 @@
<string name="error_adding_account">خطأ: %s</string>
<string name="url">رابط</string>
<string name="widget_due_date_hidden">مخفي</string>
<string name="widget_footer">تذييل</string>
<string name="show_unstarted">إظهار الغير مبدوء</string>
<string name="discard">رمي</string>
<string name="menu_discard_changes">رمي التغييرات</string>
@ -565,7 +562,6 @@
<string name="bundle_notifications">حزمة الاشعارات</string>
<string name="use_locale_default">استخدم الإعدادات المحلية الافتراضية</string>
<string name="clear_completed_tasks_confirmation">أزل المهام المتممة؟</string>
<string name="widget_due_date_reschedule">أعد جدولة المهمة</string>
<string name="hardware_support_required">مطلوب دعم الأجهزة</string>
<string name="restart_required">أعد تشغيل التطبيق ليأخذ هذا التغيير تأثيره</string>
<string name="map_theme">قالب الخارطة</string>

@ -517,7 +517,6 @@
<string name="caldav_selection_description">Синхронизиране посредством отворени стандарти</string>
<string name="choose_synchronization_service">Избор на услуга</string>
<string name="subtasks_multilevel_google_task">Вложени задачи не се поддържат от Google Tasks</string>
<string name="widget_on_click">При докосване</string>
<string name="whats_new">Какво е новото</string>
<string name="no_app_found">Липсва приложение, което да обработи тази заявка</string>
<string name="etesync_selection_description">Синхронизация, шифрована от край до край</string>
@ -527,14 +526,12 @@
<string name="now">Сега</string>
<string name="filter_criteria_unstarted">Незапочнати</string>
<string name="opacity_row">Непрозрачност на реда</string>
<string name="widget_footer">Долен колонтитул</string>
<string name="widget_due_date_below_title">Под заглавието</string>
<string name="date_shortcut_tomorrow_evening">Утре вечер</string>
<string name="date_shortcut_tomorrow_night">Утре през нощта</string>
<string name="show_unstarted">Незапочнати</string>
<string name="menu_discard_changes">Отхвърляне на промените</string>
<string name="opacity_header">Непрозрачност на заглавката</string>
<string name="widget_due_date_reschedule">Пренасрочване на задачата</string>
<string name="widget_due_date_after_title">След заглавието</string>
<string name="widget_open_list">Отваряне на списък</string>
<string name="url">Адрес</string>
@ -550,7 +547,6 @@
<string name="background_location_permission_required">Tasks събира данни за местоположението, за да прави напомняния на тази основа, дори и когато приложението е затворено или не се използва.</string>
<string name="when_started">При започване</string>
<string name="map_theme">Тема на картата</string>
<string name="widget_do_nothing">Без действие</string>
<string name="wearable_notifications_summary">Показване на известия на носимото устройство</string>
<string name="color_wheel">Цветно колело</string>
<string name="notification_troubleshooting_summary">Докоснете тук, ако имате проблеми с известията</string>

@ -471,11 +471,7 @@
<string name="widget_due_date_hidden">Skrytý</string>
<string name="widget_due_date_below_title">Pod názvem</string>
<string name="widget_due_date_after_title">Za názvem</string>
<string name="widget_on_click">Při kliknutí na</string>
<string name="widget_due_date_reschedule">Změnit termín úkolu</string>
<string name="widget_open_list">Otevřít seznam</string>
<string name="widget_do_nothing">Nic nedělat</string>
<string name="widget_footer">Zápatí</string>
<string name="widget_show_dividers">Zobrazit oddělovače</string>
<string name="whats_new">Co je nového</string>
<string name="pick_this_location">Vybrat toto místo</string>

@ -253,11 +253,7 @@
<string name="widget_due_date_hidden">Skjult</string>
<string name="widget_due_date_below_title">Under titel</string>
<string name="widget_due_date_after_title">Efter titel</string>
<string name="widget_due_date_reschedule">Udsæt opgave</string>
<string name="widget_open_list">Åbn liste</string>
<string name="widget_do_nothing">Gør ingenting</string>
<string name="widget_on_click">Ved tryk</string>
<string name="widget_footer">Fodbjælke</string>
<string name="widget_row_settings">Rækkeindstillinger</string>
<string name="widget_header_settings">Topbjælkeindstillinger</string>
<string name="widget_settings">Widget-indstillinger</string>

@ -463,9 +463,7 @@
<string name="select_all">Alles auswählen</string>
<string name="share">Teilen</string>
<string name="hide_check_button">Erledigt-Schaltfläche ausblenden</string>
<string name="widget_on_click">Nach Klick</string>
<string name="widget_show_dividers">Trennlinien anzeigen</string>
<string name="widget_do_nothing">Nichts tun</string>
<string name="opacity_footer">Deckkraft der Fußzeile</string>
<string name="opacity_row">Deckkraft der Zeilen</string>
<string name="opacity_header">Deckkraft der Kopfleiste</string>
@ -483,8 +481,6 @@
<string name="auto_dismiss_datetime_edit_summary">Automatisch schließen wenn aus der Aufgabenbearbeitung gewählt</string>
<string name="auto_dismiss_datetime_list_summary">Automatisch schließen wenn aus der Aufgabenliste gewählt</string>
<string name="auto_dismiss_datetime_list">Aufgabenliste</string>
<string name="widget_due_date_reschedule">Aufgabe verlegen</string>
<string name="widget_footer">Fußzeile</string>
<string name="custom_filter_and">UND</string>
<string name="custom_filter_not">NICHT</string>
<string name="custom_filter_or">ODER</string>

@ -215,7 +215,6 @@
<string name="delete_multiple_tasks_confirmation">%s forigitaj</string>
<string name="copy_multiple_tasks_confirmation">%s kopitaj</string>
<string name="widget_open_list">Malfermu liston</string>
<string name="widget_do_nothing">Faru nenion</string>
<string name="widget_settings">Agordoj de fenestraĵo</string>
<string name="restart_later">Poste</string>
<string name="language">Lingvo</string>
@ -581,8 +580,6 @@
<string name="repeats_single_until">Ripetiĝas %1$s ĝis %2$s</string>
<string name="got_it">Komprenite!</string>
<string name="backups_ignore_warnings_summary">Ignori avertojn pri savkopioj se vi ne bezonas savkopiojn aŭ jam havas propran savkopian solvon</string>
<string name="widget_footer">Piedlinio</string>
<string name="widget_due_date_reschedule">Tempe replani taskon</string>
<string name="use_locale_default">Uzi defaŭltan lokaĵaron</string>
<string name="error_adding_account">Eraro: %s</string>
<string name="logout_warning">Ĉiu de la datumo de ĉi tiu konto foriĝos el via aparato</string>
@ -720,7 +717,6 @@
<string name="account_not_included">Ne inkluzivite per \'Elekti propran prezon\' abonoj</string>
<string name="upgrade_desktop_access_description">Sinkronigi kun klientoj de ekstera liveranto kiel Outlook kaj Apple Reminders</string>
<string name="pro_dashclock_extension">Dashclock kromprogramo</string>
<string name="widget_on_click">Post alklaki</string>
<string name="tasker_list_notification">Sciigo de listo</string>
<string name="list_members">Anoj de la listo</string>
<string name="auto_dismiss_datetime_edit">Redakti taskon</string>

@ -481,8 +481,6 @@
<string name="settings_default">Predefinido</string>
<string name="widget_id">Widget ID: %d</string>
<string name="widget_open_list">Lista abierta</string>
<string name="widget_do_nothing">No hacer nada</string>
<string name="widget_on_click">Al pulsar</string>
<string name="widget_show_menu">Mostrar menú</string>
<string name="widget_due_date_hidden">Oculto</string>
<string name="widget_due_date_below_title">Debajo del título</string>
@ -497,8 +495,6 @@
<string name="auto_dismiss_datetime_edit">Edición de tareas</string>
<string name="auto_dismiss_datetime_list_summary">Cierre automático al seleccionar de la lista de tareas</string>
<string name="auto_dismiss_datetime_list">Lista de tareas</string>
<string name="widget_due_date_reschedule">Reprogramar tarea</string>
<string name="widget_footer">Pie de página</string>
<string name="custom_filter_not">NO</string>
<string name="custom_filter_or">O</string>
<string name="custom_filter_and">Y</string>

@ -491,11 +491,7 @@
<string name="widget_due_date_hidden">Ezkutatuta</string>
<string name="widget_due_date_below_title">Izenburuaren azpian</string>
<string name="widget_due_date_after_title">Izenburua eta gero</string>
<string name="widget_due_date_reschedule">Birprogramatu zeregina</string>
<string name="widget_open_list">Ireki zerrenda</string>
<string name="widget_do_nothing">Ez egin ezer</string>
<string name="widget_on_click">Sakatzean</string>
<string name="widget_footer">Oina</string>
<string name="opacity_footer">Oinaren opakotasuna</string>
<string name="opacity_row">Errenkadaren opakotasuna</string>
<string name="opacity_header">Goiburuaren opakotasuna</string>

@ -496,11 +496,7 @@
<string name="widget_due_date_hidden">Piilotettu</string>
<string name="widget_due_date_below_title">Otsikon alle</string>
<string name="widget_due_date_after_title">Otsikon jälkeen</string>
<string name="widget_due_date_reschedule">Uudelleen aikatauluta tehtävä</string>
<string name="widget_open_list">Avaa lista</string>
<string name="widget_do_nothing">Älä tee mitään</string>
<string name="widget_on_click">Klikattaessa</string>
<string name="widget_footer">Alatunniste</string>
<string name="opacity_row">Rivin läpinäkyvyys</string>
<string name="opacity_header">Ylätunnisteen läpinäkyvyys</string>
<string name="sort_modified_group">Muutettu %s</string>

@ -482,8 +482,6 @@
<string name="widget_id">Identifiant du widget: %d</string>
<string name="widget_show_menu">Afficher le menu</string>
<string name="widget_open_list">Ouvrir la liste</string>
<string name="widget_do_nothing">Ne rien faire</string>
<string name="widget_on_click">Lors d\'un appui</string>
<string name="widget_due_date_hidden">Masquée</string>
<string name="widget_due_date_below_title">Sous le titre</string>
<string name="widget_due_date_after_title">Après le titre</string>
@ -497,8 +495,6 @@
<string name="auto_dismiss_datetime_edit">Édition de tâche</string>
<string name="auto_dismiss_datetime_list_summary">Fermeture automatique lors de la sélection depuis la liste des tâches</string>
<string name="auto_dismiss_datetime_list">Liste des tâches</string>
<string name="widget_due_date_reschedule">Replanifier la tâche</string>
<string name="widget_footer">Pied de page</string>
<string name="custom_filter_criteria">Critères de filtrage</string>
<string name="custom_filter_not">NON</string>
<string name="custom_filter_or">OU</string>

@ -247,7 +247,6 @@
<string name="filter_no_priority">Sen prioridade</string>
<string name="repeat_type_due">data de fin</string>
<string name="geofence_radius">Radio</string>
<string name="widget_due_date_reschedule">Reprogramar a tarefa</string>
<string name="use_locale_default">Usar predefinido do sistema</string>
<string name="add_account">Engadir conta</string>
<string name="error_adding_account">Erro: %s</string>
@ -400,8 +399,6 @@
<string name="subtasks">Subtarefas</string>
<string name="menu_discard_changes">Descartar mudanzas</string>
<string name="username_required">Precísase nome de usuario</string>
<string name="widget_on_click">Co clic</string>
<string name="widget_do_nothing">Non facer nada</string>
<string name="widget_open_list">Abrir lista</string>
<string name="user">Usuario/a</string>
<string name="repeats_hourly">por hora</string>
@ -439,7 +436,6 @@
<string name="app_passwords_more_info">Sincroniza as tarefas e os calendarios en aplicativos de terceiros para escritorio ou dispositivos móbiles. Preme aquí para máis información</string>
<string name="generate_new_password">Xerar novo contrasinal</string>
<string name="upgrade_desktop_access">Acceso ao escritorio</string>
<string name="widget_footer">Rodapé</string>
<string name="repeats_weekly">por semana</string>
<string name="version_string">Versión %s</string>
<string name="gtasks_GPr_header">Google Tasks</string>

@ -351,11 +351,7 @@
<string name="widget_due_date_hidden">Skriveno</string>
<string name="widget_due_date_below_title">Ispod naslova</string>
<string name="widget_due_date_after_title">Nakon naslova</string>
<string name="widget_due_date_reschedule">Promijeni termin zadatka</string>
<string name="widget_open_list">Otvori popis</string>
<string name="widget_do_nothing">Nemoj ništa raditi</string>
<string name="widget_on_click">Pri pritiskanju</string>
<string name="widget_footer">Podnožje</string>
<string name="widget_row_settings">Postavke redaka</string>
<string name="widget_header_settings">Postavke zaglavlja</string>
<string name="hardware_support_required">Podrška za hardver je obavezna</string>

@ -467,8 +467,6 @@
<string name="widget_due_date_below_title">Cím alatt</string>
<string name="widget_due_date_after_title">Cím után</string>
<string name="widget_open_list">Lista megnyitása</string>
<string name="widget_do_nothing">Nincs művelet</string>
<string name="widget_on_click">Kattintásra</string>
<string name="opacity_footer">Lábléc átlátszóság</string>
<string name="opacity_row">Sor átlátszóság</string>
<string name="opacity_header">Fejléc átlátszóság</string>
@ -479,8 +477,6 @@
<string name="auto_dismiss_datetime_edit">Feladat szerkesztés</string>
<string name="auto_dismiss_datetime_list_summary">Automatikus bezárás a feladat listáról történő választáskor</string>
<string name="auto_dismiss_datetime_list">Feladat lista</string>
<string name="widget_due_date_reschedule">Task újraütemezése</string>
<string name="widget_footer">Lábléc</string>
<string name="custom_filter_not">NEM</string>
<string name="custom_filter_or">VAGY</string>
<string name="custom_filter_and">ÉS</string>

@ -383,9 +383,7 @@
<string name="tasker_list_notification">Daftar Notifikasi</string>
<string name="widget_due_date_hidden">Tersembunyi</string>
<string name="widget_due_date_after_title">Sesudah judul</string>
<string name="widget_due_date_reschedule">Jadwalkan ulang tugas</string>
<string name="widget_open_list">Buka daftar</string>
<string name="widget_do_nothing">Jangan lakukan apapun</string>
<string name="theme_system_default">Bawaan sistem</string>
<string name="default_recurrence">Pengulangan bawaan</string>
<string name="default_tags">Tag bawaan</string>
@ -549,8 +547,6 @@
<string name="repeats_plural_on">Ulangi setiap %1$s pada %2$s</string>
<string name="bundle_notifications">Notifikasi Bundel</string>
<string name="widget_due_date_below_title">Dibawah judul</string>
<string name="widget_on_click">Pada klik</string>
<string name="widget_footer">Tajuk bawah</string>
<string name="opacity_footer">Opasitas tajuk bawah</string>
<string name="opacity_row">Opasitas baris</string>
<string name="opacity_header">Opasitas tajuk</string>

@ -497,11 +497,7 @@
<string name="widget_due_date_hidden">Nascosto</string>
<string name="widget_due_date_below_title">Sotto il titolo</string>
<string name="widget_due_date_after_title">Dopo il titolo</string>
<string name="widget_due_date_reschedule">Pianifica di nuovo l\'attività</string>
<string name="widget_open_list">Apri lista</string>
<string name="widget_do_nothing">Non fare nulla</string>
<string name="widget_on_click">Al clic</string>
<string name="widget_footer">Piè di pagina</string>
<string name="yesterday_lowercase">ieri</string>
<string name="tomorrow_lowercase">domani</string>
<string name="today_lowercase">oggi</string>

@ -493,11 +493,7 @@
<string name="choose_synchronization_service">בחירת פלטפורמה</string>
<string name="enter_tag_name">נא למלא שם תגית</string>
<string name="subtasks_multilevel_google_task">תת־משימות מקוננות אינן נתמכות על ידי Google Tasks</string>
<string name="widget_due_date_reschedule">לתזמן משימה מחדש</string>
<string name="widget_open_list">לפתוח רשימה</string>
<string name="widget_do_nothing">לא לעשות כלום</string>
<string name="widget_on_click">בלחיצה</string>
<string name="widget_footer">כותרת תחתונה</string>
<string name="theme_system_default">בררת המחדל של המערכת</string>
<string name="opacity_footer">אטימות הכותרת התחתונה</string>
<string name="opacity_header">אטימות כותרת</string>

@ -416,11 +416,9 @@
<string name="repeat_monthly_fifth_week">第五週</string>
<string name="help_and_feedback">ヘルプとフィードバック</string>
<string name="widget_open_list">リストをひらく</string>
<string name="widget_do_nothing">何もしない</string>
<string name="theme_system_default">システムデフォルト</string>
<string name="opacity_row">行の透明度</string>
<string name="opacity_footer">フッターの透明度</string>
<string name="widget_footer">フッター</string>
<string name="opacity_header">ヘッダーの透明度</string>
<string name="map_theme">地図のテーマ</string>
<string name="default_tags">デフォルトのタグ</string>
@ -487,7 +485,6 @@
<string name="always_display_full_date">完全な日付を表示</string>
<string name="astrid_sort_order_summary">「マイタスク」「今日」そしてタグにおいて Astrid の手動並べ替えモードを有効にします。この並べ替えモードは将来のアップデートで「マイ設定」に置き換えられる予定です</string>
<string name="CFC_startBefore_name">…に着手</string>
<string name="widget_due_date_reschedule">タスクを再計画</string>
<plurals name="reminder_minutes">
<item quantity="other"></item>
</plurals>
@ -660,7 +657,6 @@
<string name="disable_battery_optimizations">バッテリー最適化を無効にする</string>
<string name="background_location">バックグラウンド時の位置情報</string>
<string name="above_average">平均以上</string>
<string name="widget_on_click">クリック時</string>
<string name="filter_any_start_date">着手日時あり</string>
<string name="chip_appearance">チップの外観</string>
<string name="desaturate_colors_summary_on">ダークテーマの使用時に彩度が下がります</string>

@ -494,12 +494,8 @@
<string name="action_new_task">새 할일</string>
<string name="widget_due_date_hidden">숨기기</string>
<string name="widget_due_date_below_title">제목 밑</string>
<string name="widget_due_date_reschedule">할일 기한 변경</string>
<string name="widget_open_list">목록 열기</string>
<string name="widget_do_nothing">아무 동작 하지 않음</string>
<string name="widget_on_click">클릭 시</string>
<string name="opacity_footer">푸터 불투명도</string>
<string name="widget_footer">푸터</string>
<string name="opacity_row">줄 불투명도</string>
<string name="opacity_header">헤더 불투명도</string>
<string name="widget_show_dividers">할일 구분선 표시</string>

@ -432,7 +432,6 @@
<string name="sort_created">Pagal sukūrimo datą</string>
<string name="custom_filter_criteria">Filtravimo kriterijai</string>
<string name="auto_dismiss_datetime_list_summary">Automatiškai uždaryti renkantis iš užduočių sąrašo</string>
<string name="widget_do_nothing">Nieko nedaryti</string>
<string name="widget_due_date_below_title">Žemiau pavadinimo</string>
<string name="widget_due_date_after_title">Po pavadinimo</string>
<string name="opacity_footer">Apatinės antraštės permatomumas</string>
@ -463,7 +462,6 @@
<string name="yesterday_lowercase">vakar</string>
<string name="tomorrow_lowercase">rytoj</string>
<string name="today_lowercase">šiandien</string>
<string name="widget_footer">Apatinė antraštė</string>
<string name="widget_due_date_hidden">Paslėpta</string>
<string name="settings_default">Numatyta</string>
<string name="share">Dalintis</string>
@ -569,8 +567,6 @@
<string name="background_location_permission_required">Tasks renka vietovės duomenis, kad įgalintų priminimus pagal vietovę, net kai programa uždaryta arba nenaudojama.</string>
<string name="help_and_feedback">Pagalba ir atsiliepimai</string>
<string name="error_adding_account">Klaida: %s</string>
<string name="widget_due_date_reschedule">Nukelti užduotį</string>
<string name="widget_on_click">Paspaudus</string>
<string name="default_tags">Numatytos etiketės</string>
<string name="location_radius_meters">%s m.</string>
<string name="subtasks">Antrinės užduotys</string>

@ -432,8 +432,6 @@
<string name="widget_id">Miniprograms-ID: %d</string>
<string name="color_wheel">Fargehjul</string>
<string name="widget_open_list">Åpne liste</string>
<string name="widget_do_nothing">Ikke gjør noe</string>
<string name="widget_on_click">Ved klikk</string>
<string name="menu_discard_changes">Forkast endringer</string>
<string name="widget_show_menu">Vis meny</string>
<string name="preferences_look_and_feel">Utseende og oppførsel</string>
@ -458,7 +456,6 @@
<string name="widget_due_date_hidden">Skjult</string>
<string name="widget_due_date_below_title">Under tittelen</string>
<string name="widget_due_date_after_title">Etter tittel</string>
<string name="widget_footer">Bunntekst</string>
<string name="opacity_footer">Bunntekst-dekkevne</string>
<string name="opacity_row">Rad-dekkevne</string>
<string name="opacity_header">Topptekst-dekkevne</string>
@ -494,7 +491,6 @@
<string name="filter_today_only">Kun i dag</string>
<string name="etesync_selection_description">Ende-til-ende-kryptert synkronisering</string>
<string name="caldav_selection_description">Synkronisering basert på åpne internettstandarder</string>
<string name="widget_due_date_reschedule">Endre gjøremålstidspunkt</string>
<string name="sort_created">Etter opprettelsestid</string>
<string name="auto_dismiss_datetime_list">Gjøremålsliste</string>
<string name="auto_dismiss_datetime_list_summary">Lukk automatisk når valgt fra gjøremålsliste</string>

@ -3,6 +3,9 @@
<color name="window_background">@color/md_background_dark</color>
<color name="content_background">@color/md_background_dark</color>
<color name="dialog_background">@color/md_background_dark</color>
<color name="widget_text_follow_system">@color/white_87</color>
<color name="widget_secondary_text_follow_system">@color/white_60</color>
<color name="widget_background_follow_system">@color/md_background_dark</color>
<color name="icon_tint">@color/icon_tint_dark</color>
<color name="icon_tint_with_alpha">@color/icon_tint_dark_alpha</color>
<color name="drawer_color_selected">@color/drawer_background_dark_selected</color>

@ -466,8 +466,6 @@
<string name="widget_id">Widget ID: %d</string>
<string name="widget_show_menu">Menu tonen</string>
<string name="widget_open_list">Lijst openen</string>
<string name="widget_do_nothing">Niets doen</string>
<string name="widget_on_click">Bij klikken</string>
<string name="widget_due_date_hidden">Verborgen</string>
<string name="widget_due_date_below_title">Onder titel</string>
<string name="widget_due_date_after_title">Na titel</string>
@ -481,8 +479,6 @@
<string name="auto_dismiss_datetime_widget">Widget</string>
<string name="auto_dismiss_datetime_edit">Taken bewerken</string>
<string name="auto_dismiss_datetime_list">Takenlijst</string>
<string name="widget_due_date_reschedule">Taak opnieuw inplannen</string>
<string name="widget_footer">Voettekst</string>
<string name="custom_filter_not">NIET</string>
<string name="custom_filter_or">OF</string>
<string name="custom_filter_and">EN</string>

@ -95,7 +95,6 @@
<string name="theme_light">ହାଲୁକା</string>
<string name="theme_dark">ଗାଢ଼</string>
<string name="theme_day_night">ଦିନ/ରାତି</string>
<string name="widget_do_nothing">କିଛି କରନ୍ତୁନି</string>
<string name="date_and_time">ତାରିଖ ଓ ସମୟ</string>
<string name="start_of_week">ସପ୍ତାହ ଆରମ୍ଭ</string>
<string name="user">ଉପଭୋକ୍ତା</string>

@ -484,11 +484,7 @@
<string name="widget_due_date_hidden">Ukryte</string>
<string name="widget_due_date_below_title">Poniżej tytułu</string>
<string name="widget_due_date_after_title">Po tytule</string>
<string name="widget_due_date_reschedule">Przesuń zadanie</string>
<string name="widget_open_list">Otwórz listę</string>
<string name="widget_do_nothing">Nie rób nic</string>
<string name="widget_on_click">Po kliknięciu</string>
<string name="widget_footer">Stopka</string>
<string name="opacity_footer">Nieprzezroczystość stopki</string>
<string name="opacity_row">Nieprzezroczystość wierszy</string>
<string name="opacity_header">Nieprzezroczystość nagłówka</string>

@ -454,11 +454,7 @@
<string name="widget_due_date_hidden">Escondido</string>
<string name="widget_due_date_below_title">Abaixo do título</string>
<string name="widget_due_date_after_title">Depois do título</string>
<string name="widget_due_date_reschedule">Reagendar tarefa</string>
<string name="widget_open_list">Abrir a lista</string>
<string name="widget_do_nothing">Não fazer nada</string>
<string name="widget_on_click">Quando clicado</string>
<string name="widget_footer">Rodapé</string>
<string name="theme_system_default">Padrão do Sistema</string>
<string name="menu_discard_changes">Descartar mudanças</string>
<string name="CFC_tag_text">Etiqueta: \?</string>

@ -426,11 +426,7 @@
<string name="auto_dismiss_datetime_edit">Editar tarefa</string>
<string name="auto_dismiss_datetime_list_summary">Fechar quando escolher da lista de tarefas automaticamente</string>
<string name="auto_dismiss_datetime_list">Lista de tarefas</string>
<string name="widget_due_date_reschedule">Reagendar tarefa</string>
<string name="widget_footer">Rodapé</string>
<string name="widget_open_list">Abrir lista</string>
<string name="widget_do_nothing">Não fazer nada</string>
<string name="widget_on_click">Quando clicado</string>
<string name="widget_due_date_hidden">Escondido</string>
<string name="widget_due_date_below_title">Abaixo do título</string>
<string name="widget_due_date_after_title">Depois do título</string>

@ -159,11 +159,7 @@
<string name="widget_due_date_hidden">Ascuns</string>
<string name="widget_due_date_below_title">Sub titlul</string>
<string name="widget_due_date_after_title">După titlu</string>
<string name="widget_due_date_reschedule">Reprogramează sarcina</string>
<string name="widget_open_list">Listă deschisă</string>
<string name="widget_do_nothing">Nu face nimic</string>
<string name="widget_on_click">La clic</string>
<string name="widget_footer">Subsol</string>
<string name="widget_row_settings">Setări de rânduri</string>
<string name="widget_header_settings">Setări de antet</string>
<string name="widget_settings">Setări Widget</string>

@ -490,11 +490,7 @@
<string name="widget_due_date_hidden">Спрятано</string>
<string name="widget_due_date_below_title">Под названием</string>
<string name="widget_due_date_after_title">После названия</string>
<string name="widget_due_date_reschedule">Перенести задачу</string>
<string name="widget_open_list">Открыть список</string>
<string name="widget_do_nothing">Ничего не делать</string>
<string name="widget_on_click">По щелчку</string>
<string name="widget_footer">Футер</string>
<string name="opacity_footer">Непрозрачность футера</string>
<string name="opacity_row">Непрозрачность строки</string>
<string name="opacity_header">Непрозрачность заголовка</string>

@ -37,7 +37,6 @@
<string name="widget_due_date_hidden">සඟවන්න</string>
<string name="widget_due_date_below_title">මාතෘකාවට පහළින්</string>
<string name="widget_due_date_after_title">මාතෘකාවෙන් පසු</string>
<string name="widget_on_click">ක්ලික් කරන විට</string>
<string name="widget_settings">විජට් සැකසුම්</string>
<string name="map_theme">සිතියම් තේමාව</string>
<string name="default_recurrence">පෙරනිමි පුනරාවර්තනය</string>
@ -185,10 +184,7 @@
<string name="delete_multiple_tasks_confirmation">%s මකා දමන ලදි</string>
<string name="copy_multiple_tasks_confirmation">%s පිටපත් කරන ලදි</string>
<string name="clear_completed_tasks_confirmation">සම්පුර්ණ කරන ලද කාර්යයන් ඉවත් කරනවාද\?</string>
<string name="widget_due_date_reschedule">කාර්යය නැවත සකස් කිරීම</string>
<string name="widget_open_list">ලැයිස්තුව විවෘත කරන්න</string>
<string name="widget_do_nothing">කිසිවක් නොකරන්න</string>
<string name="widget_footer">පාදකය</string>
<string name="widget_row_settings">පේළි සැකසුම්</string>
<string name="widget_header_settings">ශීර්ෂ සැකසුම්</string>
<string name="hardware_support_required">දෘඩාංග සහාය අවශ්‍යයි</string>

@ -433,7 +433,6 @@
<string name="help_and_feedback">Hjälp &amp; återkoppling</string>
<string name="widget_due_date_hidden">Dold</string>
<string name="widget_open_list">Öppna lista</string>
<string name="widget_do_nothing">Gör ingenting</string>
<string name="date_shortcut_tomorrow_night">Imorgon natt</string>
<string name="date_shortcut_tomorrow_evening">Imorgon kväll</string>
<string name="widget_show_menu">Visa meny</string>
@ -591,9 +590,6 @@
<string name="subscription">Abonnemang</string>
<string name="widget_due_date_below_title">Under titeln</string>
<string name="widget_due_date_after_title">Efter titeln</string>
<string name="widget_due_date_reschedule">Planera om uppgift</string>
<string name="widget_on_click">När du klickar på</string>
<string name="widget_footer">Sidfot</string>
<string name="theme_system_default">Systemets standardinställning</string>
<string name="map_theme">Tema för kartor</string>
<string name="opacity_footer">Opacitet för sidfot</string>

@ -292,11 +292,7 @@
<string name="widget_due_date_hidden">மறைக்கப்பட்டுள்ளது</string>
<string name="widget_due_date_below_title">தலைப்புக்கு கீழே</string>
<string name="widget_due_date_after_title">தலைப்புக்குப் பிறகு</string>
<string name="widget_due_date_reschedule">பணியை மறுபரிசீலனை செய்யுங்கள்</string>
<string name="widget_open_list">திறந்த பட்டியல்</string>
<string name="widget_do_nothing">எதுவும் செய்ய வேண்டாம்</string>
<string name="widget_on_click">கிளிக் செய்தால்</string>
<string name="widget_footer">அடிக்குறிப்பு</string>
<string name="widget_row_settings">வரிசை அமைப்புகள்</string>
<string name="widget_header_settings">தலைப்பு அமைப்புகள்</string>
<string name="widget_settings">விட்ஜெட் அமைப்புகள்</string>

@ -279,11 +279,7 @@
<string name="widget_due_date_hidden">ซ่อนเร้น</string>
<string name="widget_due_date_below_title">ใต้ชื่อเรื่อง</string>
<string name="widget_due_date_after_title">หลังชื่อเรื่อง</string>
<string name="widget_due_date_reschedule">กำหนดการงานใหม่</string>
<string name="widget_open_list">เปิดรายการ</string>
<string name="widget_do_nothing">ไม่ต้องทําอะไร</string>
<string name="widget_on_click">เมื่อคลิก</string>
<string name="widget_footer">ส่วนท้าย</string>
<string name="widget_row_settings">การตั้งค่าแถว</string>
<string name="widget_header_settings">การตั้งค่าส่วนหัว</string>
<string name="widget_settings">การตั้งค่าวิดเจ็ต</string>

@ -468,21 +468,17 @@
<string name="widget_due_date_below_title">Başlığın altında</string>
<string name="widget_due_date_after_title">Başlıktan sonra</string>
<string name="widget_open_list">Listeyi aç</string>
<string name="widget_do_nothing">Bir şey yapma</string>
<string name="widget_on_click">Tıkladığında</string>
<string name="opacity_footer">Alt bölüm opaklığı</string>
<string name="opacity_row">Satır opaklığı</string>
<string name="opacity_header">Başlık opaklığı</string>
<string name="widget_show_dividers">Ayırıcıları göster</string>
<string name="widget_show_menu">Menüyü göster</string>
<string name="auto_dismiss_datetime_edit">Görev düzenleme</string>
<string name="widget_due_date_reschedule">Görevi yeniden zamanla</string>
<string name="auto_dismiss_datetime_widget_summary">Widget\'tan seçerken kendiliğinden kapat</string>
<string name="auto_dismiss_datetime_widget">Widget</string>
<string name="auto_dismiss_datetime_edit_summary">Görev düzenlemeden seçerken kendiliğinden kapat</string>
<string name="auto_dismiss_datetime_list_summary">Görev listesinden seçerken kendiliğinden kapat</string>
<string name="auto_dismiss_datetime_list">Görev listesi</string>
<string name="widget_footer">Alt bölüm</string>
<string name="filter_overdue">Geciken</string>
<string name="filter_any_due_date">Herhangi bitiş tarihi</string>
<string name="filter_eisenhower_box_4">Önemli veya ivedi değil</string>

@ -575,11 +575,7 @@
<string name="widget_due_date_hidden">Сховано</string>
<string name="widget_due_date_below_title">Під назвою</string>
<string name="widget_due_date_after_title">Після назви</string>
<string name="widget_due_date_reschedule">Перенести завдання</string>
<string name="widget_open_list">Відкрити список</string>
<string name="widget_do_nothing">Нічого не робити</string>
<string name="widget_on_click">Після натискання</string>
<string name="widget_footer">Спід</string>
<string name="opacity_footer">Непрозорість споду</string>
<string name="opacity_row">Непрозорість строки</string>
<string name="opacity_header">Непрозорість заголовка</string>

@ -326,7 +326,6 @@
<string name="discard_changes">تبدیلیاں مسترد کریں؟</string>
<string name="date_shortcut_must_come_after">%1$s کو %2$sسے پہلے آنا چاہیے</string>
<string name="date_shortcut_must_come_before">%1$s کو %2$s سے پہلے آنا چایئے</string>
<string name="widget_footer">نچلا حصہ</string>
<string name="widget_row_settings">قطار کی سیٹنگ</string>
<string name="widget_header_settings">ہیڈر کی سیٹنگ</string>
<string name="widget_settings">ویجٹ سیٹنگ</string>
@ -387,10 +386,7 @@
<string name="widget_due_date_hidden">چھپا ہوا</string>
<string name="widget_due_date_below_title">عنوان کے نیچے</string>
<string name="widget_due_date_after_title">عنوان کے بعد</string>
<string name="widget_due_date_reschedule">ٹاسک ری شیڈیول کریں</string>
<string name="widget_open_list">فہرست کھولیں</string>
<string name="widget_do_nothing">کچھ نہ کریں</string>
<string name="widget_on_click">کلک پر</string>
<string name="tasker_create_task">ٹاسک بنائیں</string>
<string name="repeat_monthly_last_week">آخری</string>
<string name="repeat_monthly_fifth_week">پانچواں</string>

@ -331,11 +331,7 @@
<string name="widget_due_date_hidden">Ẩn</string>
<string name="widget_due_date_below_title">Dưới tiêu đề</string>
<string name="widget_due_date_after_title">Sau tiêu đề</string>
<string name="widget_due_date_reschedule">Lên lịch lại công việc</string>
<string name="widget_open_list">Mở danh sách</string>
<string name="widget_do_nothing">Không làm gì cả</string>
<string name="widget_on_click">Khi nhấn</string>
<string name="widget_footer">Chân trang</string>
<string name="widget_row_settings">Cài đặt hàng</string>
<string name="widget_header_settings">Cài đặt tiêu đề</string>
<string name="widget_settings">Cài đặt tiện ích</string>

@ -456,8 +456,6 @@
<string name="widget_id">部件ID%d</string>
<string name="widget_show_menu">显示菜单</string>
<string name="widget_open_list">打开列表</string>
<string name="widget_do_nothing">什么也不做</string>
<string name="widget_on_click">点击时</string>
<string name="auto_dismiss_datetime_widget_summary">从小部件中选择时自动关闭</string>
<string name="auto_dismiss_datetime_widget">小部件</string>
<string name="auto_dismiss_datetime_edit_summary">从任务编辑中选择时自动关闭</string>
@ -467,8 +465,6 @@
<string name="widget_due_date_hidden">隐藏</string>
<string name="widget_due_date_below_title">标题下方</string>
<string name="widget_due_date_after_title">标题后</string>
<string name="widget_due_date_reschedule">重新安排任务</string>
<string name="widget_footer">页脚</string>
<string name="opacity_footer">页脚不透明度</string>
<string name="opacity_row">行不透明度</string>
<string name="opacity_header">标题不透明度</string>

@ -436,11 +436,7 @@
<string name="widget_due_date_hidden">隱藏</string>
<string name="widget_due_date_below_title">標題之下</string>
<string name="widget_due_date_after_title">標題之後</string>
<string name="widget_due_date_reschedule">重新規劃工作</string>
<string name="widget_open_list">開啟清單</string>
<string name="widget_do_nothing">無動作</string>
<string name="widget_on_click">點擊時</string>
<string name="widget_footer">註腳</string>
<string name="widget_row_settings">行設定</string>
<string name="widget_header_settings">標頭設定</string>
<string name="widget_settings">小工具設定</string>

@ -138,6 +138,9 @@
<color name="window_background">@color/md_background_light</color>
<color name="content_background">@color/md_background_light</color>
<color name="dialog_background">@color/md_background_light</color>
<color name="widget_text_follow_system">@color/black_87</color>
<color name="widget_secondary_text_follow_system">@color/black_60</color>
<color name="widget_background_follow_system">@color/md_background_light</color>
<color name="icon_tint">@color/icon_tint_light</color>
<color name="icon_tint_with_alpha">@color/icon_tint_light_alpha</color>
<color name="drawer_color_selected">@color/drawer_background_light_selected</color>

@ -142,26 +142,6 @@
<item>2</item>
</string-array>
<string-array name="widget_empty_click_titles">
<item>@string/widget_do_nothing</item>
<item>@string/widget_open_list</item>
</string-array>
<string-array name="widget_empty_click_values">
<item>0</item>
<item>1</item>
</string-array>
<string-array name="widget_due_date_click_titles">
<item>@string/widget_due_date_reschedule</item>
<item>@string/widget_do_nothing</item>
</string-array>
<string-array name="widget_due_date_click_values">
<item>0</item>
<item>1</item>
</string-array>
<string-array name="widget_due_date_position_values">
<item>0</item>
<item>1</item>
@ -362,8 +342,6 @@
<string name="p_widget_show_menu">widget-show-menu-</string>
<string name="p_widget_header_opacity">widget-header-opacity-</string>
<string name="p_widget_footer_opacity">widget-empty-space-opacity-</string>
<string name="p_widget_footer_click">widget-empty-space-click-</string>
<string name="p_widget_due_date_click">widget-due-date-click-</string>
<string name="p_widget_due_date_position">widget-due-date-position-</string>
<string name="p_widget_header_spacing">widget-header-spacing-</string>
<string name="p_widget_spacing">widget-spacing-</string>

@ -399,11 +399,7 @@ File %1$s contained %2$s.\n\n
<string name="widget_settings">Widget settings</string>
<string name="widget_header_settings">Header settings</string>
<string name="widget_row_settings">Row settings</string>
<string name="widget_footer">Footer</string>
<string name="widget_on_click">On click</string>
<string name="widget_do_nothing">Do nothing</string>
<string name="widget_open_list">Open list</string>
<string name="widget_due_date_reschedule">Reschedule task</string>
<string name="widget_due_date_after_title">After title</string>
<string name="widget_due_date_below_title">Below title</string>
<string name="widget_due_date_hidden">Hidden</string>

@ -180,24 +180,4 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/widget_on_click">
<ListPreference
android:defaultValue="0"
android:key="@string/p_widget_due_date_click"
android:entryValues="@array/widget_due_date_click_values"
android:entries="@array/widget_due_date_click_titles"
android:title="@string/due_date"
android:summary="%s" />
<ListPreference
android:defaultValue="0"
android:key="@string/p_widget_footer_click"
android:entryValues="@array/widget_empty_click_values"
android:entries="@array/widget_empty_click_titles"
android:title="@string/widget_footer"
android:summary="%s" />
</PreferenceCategory>
</PreferenceScreen>

Loading…
Cancel
Save