Fix icon null pointer exceptions

pull/2947/head
Alex Baker 3 months ago
parent 6700ce5aa1
commit 64ad4ca33a

@ -51,7 +51,7 @@ android {
defaultConfig {
testApplicationId = "org.tasks.test"
applicationId = "org.tasks"
versionCode = 131100
versionCode = 131101
versionName = "13.11"
targetSdk = libs.versions.android.targetSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()

@ -5,7 +5,6 @@ import android.view.View
import android.widget.CheckedTextView
import android.widget.ImageView
import android.widget.TextView
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import androidx.core.view.isVisible
@ -15,7 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import org.tasks.R
import org.tasks.billing.Inventory
import org.tasks.compose.components.imageVectorByName
import org.tasks.compose.components.TasksIcon
import org.tasks.databinding.FilterAdapterRowBinding
import org.tasks.extensions.formatNumber
import org.tasks.filters.CaldavFilter
@ -50,10 +49,9 @@ class FilterViewHolder internal constructor(
it.icon.setContent {
val label = icon.collectAsStateWithLifecycle().value
val tint = color.collectAsStateWithLifecycle().value
Icon(
imageVector = imageVectorByName(label!!)!!,
contentDescription = label,
tint = if (tint == 0) MaterialTheme.colorScheme.onSurface else Color(tint),
TasksIcon(
label = label,
tint = if (tint == 0) MaterialTheme.colorScheme.onSurface else Color(tint)
)
}
size = it.size

@ -7,7 +7,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
@ -15,7 +14,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@ -27,7 +25,7 @@ import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.compose.IconPickerActivity.Companion.launchIconPicker
import org.tasks.compose.IconPickerActivity.Companion.registerForIconPickerResult
import org.tasks.compose.components.imageVectorByName
import org.tasks.compose.components.TasksIcon
import org.tasks.dialogs.ColorPalettePicker
import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette
import org.tasks.dialogs.ColorPickerAdapter.Palette
@ -69,15 +67,9 @@ abstract class BaseListSettingsActivity : ThemedInjectingAppCompatActivity(), To
Row(
verticalAlignment = Alignment.CenterVertically,
) {
val name = selectedIcon.collectAsStateWithLifecycle().value
val icon = imageVectorByName(name ?:defaultIcon)
if (icon != null) {
Image(
imageVector = icon,
contentDescription = name ?: defaultIcon,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
)
}
TasksIcon(
label = selectedIcon.collectAsStateWithLifecycle().value ?: defaultIcon
)
Spacer(modifier = Modifier.width(34.dp))
Text(
text = "Icon",

@ -75,7 +75,7 @@ class FilterSelectionActivity : AppCompatActivity() {
filters = if (searching) state.searchResults else state.filters,
query = state.query,
onQueryChange = { viewModel.onQueryChange(it) },
getIcon = { imageVectorByName(viewModel.getIcon(it))!! },
getIcon = { imageVectorByName(viewModel.getIcon(it)) },
getColor = { viewModel.getColor(it) },
selected = selected,
onClick = { filter ->

@ -89,7 +89,7 @@ class FilterPickerViewModel @Inject constructor(
localBroadcastManager.broadcastRefreshList()
}
fun getIcon(filter: Filter): String = filter.getIcon(inventory)
fun getIcon(filter: Filter): String? = filter.getIcon(inventory)
fun getColor(filter: Filter): Int {
if (filter.tint != 0) {

@ -3,7 +3,7 @@ package org.tasks.filters
import org.tasks.billing.Inventory
import org.tasks.themes.TasksIcons
fun Filter.getIcon(inventory: Inventory): String {
fun Filter.getIcon(inventory: Inventory): String? {
if (inventory.hasPro) {
icon?.takeIf { it.isNotBlank() }?.let { return it }
}
@ -14,6 +14,6 @@ fun Filter.getIcon(inventory: Inventory): String {
is CustomFilter -> TasksIcons.FILTER_LIST
is PlaceFilter -> TasksIcons.PLACE
else -> icon!!
else -> icon
}
}

@ -115,7 +115,8 @@ class TagPickerActivity : ThemedInjectingAppCompatActivity() {
return Color(getColor(R.color.icon_tint_with_alpha))
}
private fun getIcon(tagData: TagData): String = TagFilter(tagData).getIcon(inventory)
private fun getIcon(tagData: TagData): String =
TagFilter(tagData).getIcon(inventory) ?: TasksIcons.LABEL
/* Copy of the TagPickerActivity's companion object */
companion object {

@ -4,7 +4,6 @@ import android.content.Context
import android.widget.RemoteViews
import androidx.annotation.ColorInt
import com.mikepenz.iconics.IconicsDrawable
import org.tasks.icons.OutlinedGoogleMaterial
import com.todoroo.andlib.utility.DateUtilities
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.BuildConfig
@ -21,6 +20,7 @@ import org.tasks.filters.GtasksFilter
import org.tasks.filters.PlaceFilter
import org.tasks.filters.TagFilter
import org.tasks.filters.getIcon
import org.tasks.icons.OutlinedGoogleMaterial
import org.tasks.time.startOfDay
import org.tasks.ui.ChipListCache
import java.time.format.FormatStyle
@ -113,16 +113,17 @@ class WidgetChipProvider @Inject constructor(
private fun newChip(filter: Filter, defaultIcon: Int) =
newChip(filter.tint).apply {
setTextViewText(R.id.chip_text, filter.title)
if (filter.icon.isNullOrBlank()) {
setImageViewResource(R.id.chip_icon, defaultIcon)
} else {
val icon = filter.getIcon(inventory)
val drawable = IconicsDrawable(
context,
OutlinedGoogleMaterial.getIcon("gmo_$icon")
)
setImageViewBitmap(R.id.chip_icon, drawable.toBitmap())
}
filter
.getIcon(inventory)
?.let {
try {
OutlinedGoogleMaterial.getIcon("gmo_$it")
} catch (_: IllegalArgumentException) {
null
}
}
?.let { setImageViewBitmap(R.id.chip_icon, IconicsDrawable(context, it).toBitmap()) }
?: setImageViewResource(R.id.chip_icon, defaultIcon)
}
private fun newChip(@ColorInt color: Int = 0) = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_chip).apply {

@ -1,14 +1,40 @@
package org.tasks.compose.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import org.tasks.compose.pickers.label
fun imageVectorByName(label: String): ImageVector? =
@Composable
fun TasksIcon(
label: String?,
tint: Color = MaterialTheme.colorScheme.onSurface,
) {
Box(modifier = Modifier.size(24.dp)) {
Icon(
imageVector = remember (label) {
imageVectorByName(label)
} ?: return@Box,
contentDescription = label,
tint = tint,
)
}
}
fun imageVectorByName(label: String?): ImageVector? = label?.let {
try {
val cl = Class.forName("androidx.compose.material.icons.outlined.${label.label}Kt")
val cl = Class.forName("androidx.compose.material.icons.outlined.${it.label}Kt")
val method = cl.declaredMethods.first()
method.invoke(null, Icons.Outlined) as ImageVector
} catch (_: Throwable) {
null
}
}

@ -5,7 +5,7 @@ import org.tasks.filters.NavigationDrawerSubheader
sealed interface DrawerItem {
data class Filter(
val title: String,
val icon: String,
val icon: String?,
val color: Int = 0,
val count: Int = 0,
val shareCount: Int = 0,

@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.mandatorySystemGestures
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -56,7 +55,7 @@ import kotlinx.collections.immutable.ImmutableList
import org.jetbrains.compose.resources.stringResource
import org.tasks.compose.components.Chevron
import org.tasks.compose.components.SearchBar
import org.tasks.compose.components.imageVectorByName
import org.tasks.compose.components.TasksIcon
import org.tasks.kmp.formatNumber
import tasks.kmp.generated.resources.Res
import tasks.kmp.generated.resources.help_and_feedback
@ -194,7 +193,13 @@ internal fun FilterItem(
.clickable(onClick = onClick),
onClick = onClick,
) {
DrawerIcon(icon = item.icon, color = item.color)
TasksIcon(
label = item.icon,
tint = when (item.color) {
0 -> MaterialTheme.colorScheme.onSurface
else -> Color(color = item.color)
}
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = item.title,
@ -225,19 +230,6 @@ internal fun FilterItem(
}
}
@Composable
private fun DrawerIcon(icon: String, color: Int = 0) {
Icon(
modifier = Modifier.size(24.dp),
imageVector = imageVectorByName(icon) ?: return,
contentDescription = null,
tint = when (color) {
0 -> MaterialTheme.colorScheme.onSurface
else -> Color(color)
}
)
}
@Composable
internal fun HeaderItem(
modifier: Modifier = Modifier,

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
@ -44,7 +45,7 @@ fun CheckableIconRow(
@Composable
fun CheckableIconRow(
icon: ImageVector,
icon: ImageVector?,
tint: Color,
selected: Boolean,
onClick: () -> Unit,
@ -56,12 +57,20 @@ fun CheckableIconRow(
.fillMaxWidth()
.clickable { onClick() }
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = tint,
modifier = Modifier.padding(start = 16.dp, end = 32.dp, top = 12.dp, bottom = 12.dp),
)
Box(
modifier = Modifier
.padding(start = 16.dp, end = 32.dp, top = 12.dp, bottom = 12.dp)
.size(24.dp),
contentAlignment = Alignment.Center,
) {
if (icon != null) {
Icon(
imageVector = icon,
contentDescription = null,
tint = tint,
)
}
}
Box(modifier = Modifier.weight(1f)) {
content()
}

@ -1,7 +1,6 @@
package org.tasks.compose.pickers
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -22,7 +21,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import kotlinx.collections.immutable.ImmutableList
@ -32,7 +30,7 @@ import org.jetbrains.compose.resources.stringResource
import org.tasks.compose.components.AnimatedBanner
import org.tasks.compose.components.Chevron
import org.tasks.compose.components.SearchBar
import org.tasks.compose.components.imageVectorByName
import org.tasks.compose.components.TasksIcon
import tasks.kmp.generated.resources.Res
import tasks.kmp.generated.resources.requires_pro_subscription
import tasks.kmp.generated.resources.search
@ -150,14 +148,7 @@ fun LazyGridScope.iconGrid(
) {
items(icons, key = { it.name }) { icon ->
IconButton(onClick = { onSelected(icon) }) {
val imageVector = remember (icon.name) {
imageVectorByName(icon.name)
}
Image(
imageVector = imageVector ?: return@IconButton,
contentDescription = icon.name,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
)
TasksIcon(label = icon.name)
}
}
}

@ -37,7 +37,7 @@ fun SearchableFilterPicker(
onQueryChange: (String) -> Unit,
selected: Filter?,
onClick: (FilterListItem) -> Unit,
getIcon: @Composable (Filter) -> ImageVector,
getIcon: @Composable (Filter) -> ImageVector?,
getColor: (Filter) -> Int,
) {
LazyColumn(

Loading…
Cancel
Save