Merge branch '13.11'

pull/2977/head
Alex Baker 4 months ago
commit 9894919f49

@ -1,6 +1,8 @@
### 13.11 (2024-07-12)
### 13.11 (2024-07-13)
* New icon picker with over 2,100 icons! (pro feature)
* Fix Todo Agenda Widget integration [todoagenda/#145](https://github.com/andstatus/todoagenda/issues/145)
* Fix menu search bar on Android 10 and below [#2966](https://github.com/tasks/tasks/issues/2966)
* Update translations
* Brazilian Portuguese - Jose Delvani
* Bulgarian - @StoyanDimitrov

@ -51,7 +51,7 @@ android {
defaultConfig {
testApplicationId = "org.tasks.test"
applicationId = "org.tasks"
versionCode = 131102
versionCode = 131103
versionName = "13.11"
targetSdk = libs.versions.android.targetSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
@ -184,7 +184,6 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.room)
implementation(libs.androidx.sqlite)
implementation(libs.androidx.appcompat)
implementation(libs.iconics)
implementation(libs.markwon)

@ -26,6 +26,7 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.AndroidUtilities.atLeastR
import com.todoroo.astrid.activity.TaskEditFragment.Companion.newTaskEditFragment
import com.todoroo.astrid.adapter.SubheaderClickHandler
import com.todoroo.astrid.dao.TaskDao
@ -51,6 +52,7 @@ import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.drawer.DrawerAction
import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao
@ -145,7 +147,7 @@ class MainActivity : AppCompatActivity() {
}
val scope = rememberCoroutineScope()
TaskListDrawer(
begForMoney = state.begForMoney,
bottomSearchBar = atLeastR(),
filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems,
onClick = {
when (it) {
@ -230,42 +232,47 @@ class MainActivity : AppCompatActivity() {
}
}
},
onDrawerAction = {
viewModel.closeDrawer()
when (it) {
DrawerAction.PURCHASE ->
if (Tasks.IS_GENERIC)
context.openUri(R.string.url_donate)
else
context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
DrawerAction.SETTINGS ->
settingsRequest.launch(
Intent(
context,
MainPreferences::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity(
Intent(
context,
HelpAndFeedback::class.java
)
)
}
},
onErrorClick = {
context.startActivity(Intent(context, MainPreferences::class.java))
},
query = state.menuQuery,
onQueryChange = { viewModel.queryMenu(it) },
searchBar = {
MenuSearchBar(
begForMoney = state.begForMoney,
onDrawerAction = {
viewModel.closeDrawer()
when (it) {
DrawerAction.PURCHASE ->
if (Tasks.IS_GENERIC)
context.openUri(R.string.url_donate)
else
context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
DrawerAction.SETTINGS ->
settingsRequest.launch(
Intent(
context,
MainPreferences::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity(
Intent(
context,
HelpAndFeedback::class.java
)
)
}
},
query = state.menuQuery,
onQueryChange = { viewModel.queryMenu(it) },
)
},
)
}
}

@ -43,7 +43,7 @@ class ContentProviderDaoBlocking @Inject constructor(
private fun SQLiteStatement.toCursor(): Cursor {
val cursor = MatrixCursor(getColumnNames().toTypedArray())
while (step()) {
cursor.addRow((0 until getColumnCount()).map { getText(it) })
cursor.addRow((0 until getColumnCount()).map { getTextOrNull(it) })
}
return cursor
}

@ -2,7 +2,6 @@ package org.tasks.injection
import android.content.Context
import androidx.room.Room
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -39,7 +38,6 @@ internal class ProductionModule {
name = databaseFile.absolutePath
)
.setQueryCoroutineContext(Dispatchers.IO)
.setDriver(BundledSQLiteDriver())
.addMigrations(*Migrations.migrations(context, fileStorage))
if (!BuildConfig.DEBUG || !preferences.getBoolean(R.string.p_crash_main_queries, false)) {
builder.allowMainThreadQueries()

@ -163,7 +163,7 @@ fun SQLiteStatement.getTasks(): List<TaskContainer> {
return result
}
private fun SQLiteStatement.getTextOrNull(index: Int): String? =
fun SQLiteStatement.getTextOrNull(index: Int): String? =
if (index == -1 || isNull(index)) null else this.getText(index)
private fun SQLiteStatement.getLongOrNull(index: Int): Long? =

@ -242,15 +242,13 @@
+| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha05
+| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
+| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha05
+| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha05
+| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha05 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0
+| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0
@ -971,12 +969,6 @@
+| \--- androidx.lifecycle:lifecycle-common-java8:2.8.3 (c)
++--- androidx.lifecycle:lifecycle-viewmodel:2.8.3 (*)
++--- androidx.room:room-runtime:2.7.0-alpha05 (*)
++--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05
+| \--- androidx.sqlite:sqlite-bundled-android:2.5.0-alpha05
+| +--- androidx.sqlite:sqlite:2.5.0-alpha05 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| +--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| \--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
++--- androidx.appcompat:appcompat:1.7.0 (*)
++--- com.mikepenz:iconics-core:5.5.0-b01 (*)
++--- io.noties.markwon:core:4.6.2

@ -648,15 +648,13 @@
+| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha05
+| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
+| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha05
+| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha05
+| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha05 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05 (c)
+| | | \--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0
+| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0
@ -1195,12 +1193,6 @@
+| \--- androidx.lifecycle:lifecycle-common-java8:2.8.3 (c)
++--- androidx.lifecycle:lifecycle-viewmodel:2.8.3 (*)
++--- androidx.room:room-runtime:2.7.0-alpha05 (*)
++--- androidx.sqlite:sqlite-bundled:2.5.0-alpha05
+| \--- androidx.sqlite:sqlite-bundled-android:2.5.0-alpha05
+| +--- androidx.sqlite:sqlite:2.5.0-alpha05 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| +--- androidx.sqlite:sqlite:2.5.0-alpha05 (c)
+| \--- androidx.sqlite:sqlite-framework:2.5.0-alpha05 (c)
++--- androidx.appcompat:appcompat:1.7.0 (*)
++--- com.mikepenz:iconics-core:5.5.0-b01 (*)
++--- io.noties.markwon:core:4.6.2

@ -1,2 +1,3 @@
* New icon picker with over 2,100 icons! (pro feature)
* Fix Todo Agenda Widget integration
* Update translations

@ -85,7 +85,6 @@ androidx-preference = { module = "androidx.preference:preference", version.ref =
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-room = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room"}
androidx-sqlite = { module = "androidx.sqlite:sqlite-bundled", version = "2.5.0-alpha05" }
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" }

@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.collections.immutable.persistentListOf
import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.filters.FilterImpl
import org.tasks.filters.NavigationDrawerSubheader
@ -17,7 +18,15 @@ import org.tasks.themes.TasksTheme
fun MenuPreview() {
TasksTheme {
TaskListDrawer(
begForMoney = true,
searchBar = {
MenuSearchBar(
begForMoney = true,
onDrawerAction = {},
query = "",
onQueryChange = {}
)
},
bottomSearchBar = true,
filters = persistentListOf(
DrawerItem.Filter(
title = "My Tasks",
@ -39,11 +48,8 @@ fun MenuPreview() {
)
),
onClick = {},
onDrawerAction = {},
onAddClick = {},
onErrorClick = {},
query = "",
onQueryChange = {},
)
}
}

@ -39,6 +39,8 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -67,77 +69,56 @@ import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskListDrawer(
begForMoney: Boolean,
bottomSearchBar: Boolean,
filters: ImmutableList<DrawerItem>,
onClick: (DrawerItem) -> Unit,
onDrawerAction: (DrawerAction) -> Unit,
onAddClick: (DrawerItem.Header) -> Unit,
onErrorClick: () -> Unit,
query: String,
onQueryChange: (String) -> Unit,
searchBar: @Composable RowScope.() -> Unit,
) {
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
modifier = Modifier
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection),
.nestedScroll(
if (bottomSearchBar)
bottomAppBarScrollBehavior.nestedScrollConnection
else
topAppBarScrollBehavior.nestedScrollConnection
),
bottomBar = {
BottomAppBar(
modifier = Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
bottomAppBarScrollBehavior.state.heightOffsetLimit = -placeable.height.toFloat()
val height = placeable.height + bottomAppBarScrollBehavior.state.heightOffset
layout(placeable.width, height.roundToInt().coerceAtLeast(0)) {
placeable.place(0, 0)
}
},
containerColor = MaterialTheme.colorScheme.surface,
scrollBehavior = bottomAppBarScrollBehavior
) {
var hasFocus by remember { mutableStateOf(false) }
SearchBar(
modifier = Modifier
.onFocusChanged { hasFocus = it.hasFocus }
.padding(start = 8.dp, end = if (hasFocus) 8.dp else 0.dp, bottom = 4.dp)
.weight(1f)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
),
text = query,
onTextChange = { onQueryChange(it) },
placeHolder = stringResource(Res.string.search),
onCloseClicked = { onQueryChange("") },
onSearchClicked = {
// TODO: close keyboard
if (bottomSearchBar) {
BottomAppBar(
modifier = Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
bottomAppBarScrollBehavior.state.heightOffsetLimit =
-placeable.height.toFloat()
val height =
placeable.height + bottomAppBarScrollBehavior.state.heightOffset
layout(placeable.width, height.roundToInt().coerceAtLeast(0)) {
placeable.place(0, 0)
}
},
)
if (!hasFocus) {
if (begForMoney) {
IconButton(onClick = { onDrawerAction(DrawerAction.PURCHASE) }) {
Icon(
imageVector = Icons.Outlined.AttachMoney,
contentDescription = stringResource(Res.string.subscribe),
tint = MaterialTheme.colorScheme.onSurface,
)
containerColor = MaterialTheme.colorScheme.surface,
scrollBehavior = bottomAppBarScrollBehavior
) {
searchBar()
}
}
},
topBar = {
if (!bottomSearchBar) {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
scrollBehavior = topAppBarScrollBehavior,
title = {
Row {
searchBar()
}
}
IconButton(onClick = { onDrawerAction(DrawerAction.HELP_AND_FEEDBACK) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
contentDescription = stringResource(Res.string.help_and_feedback),
tint = MaterialTheme.colorScheme.onSurface,
)
}
IconButton(onClick = { onDrawerAction(DrawerAction.SETTINGS) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(Res.string.settings),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
)
}
}
) { contentPadding ->
@ -145,14 +126,17 @@ fun TaskListDrawer(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
bottom = maxOf(
top = if (bottomSearchBar) 0.dp else contentPadding.calculateTopPadding(),
bottom = if (bottomSearchBar)
maxOf(
WindowInsets.mandatorySystemGestures
.asPaddingValues()
.calculateBottomPadding(),
contentPadding.calculateBottomPadding()
)
) else
48.dp
),
verticalArrangement = Arrangement.Bottom,
verticalArrangement = if (bottomSearchBar) Arrangement.Bottom else Arrangement.Top,
) {
items(items = filters, key = { it.key() }) {
when (it) {
@ -294,3 +278,61 @@ private fun MenuRow(
content = content
)
}
@Composable
fun RowScope.MenuSearchBar(
begForMoney: Boolean,
onDrawerAction: (DrawerAction) -> Unit,
query: String,
onQueryChange: (String) -> Unit,
) {
var hasFocus by remember { mutableStateOf(false) }
SearchBar(
modifier = Modifier
.onFocusChanged { hasFocus = it.hasFocus }
.padding(
start = 8.dp,
end = if (hasFocus) 8.dp else 0.dp,
bottom = 4.dp
)
.weight(1f)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
),
text = query,
onTextChange = { onQueryChange(it) },
placeHolder = stringResource(Res.string.search),
onCloseClicked = { onQueryChange("") },
onSearchClicked = {
// TODO: close keyboard
},
)
if (!hasFocus) {
if (begForMoney) {
IconButton(onClick = { onDrawerAction(DrawerAction.PURCHASE) }) {
Icon(
imageVector = Icons.Outlined.AttachMoney,
contentDescription = stringResource(Res.string.subscribe),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
IconButton(onClick = { onDrawerAction(DrawerAction.HELP_AND_FEEDBACK) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
contentDescription = stringResource(Res.string.help_and_feedback),
tint = MaterialTheme.colorScheme.onSurface,
)
}
IconButton(onClick = { onDrawerAction(DrawerAction.SETTINGS) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(Res.string.settings),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
}

Loading…
Cancel
Save